diff options
Diffstat (limited to 'services')
107 files changed, 23008 insertions, 8176 deletions
diff --git a/services/java/Android.mk b/services/java/Android.mk index 5e912d6..934712c 100644 --- a/services/java/Android.mk +++ b/services/java/Android.mk @@ -5,7 +5,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - $(call all-subdir-java-files) + $(call all-subdir-java-files) \ + com/android/server/EventLogTags.logtags \ + com/android/server/am/EventLogTags.logtags LOCAL_MODULE:= services diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java index f67a7ae..87de79a 100644 --- a/services/java/com/android/server/AccessibilityManagerService.java +++ b/services/java/com/android/server/AccessibilityManagerService.java @@ -16,8 +16,7 @@ package com.android.server; -import static android.util.Config.LOGV; - +import com.android.internal.content.PackageMonitor; import com.android.internal.os.HandlerCaller; import com.android.internal.os.HandlerCaller.SomeArgs; @@ -47,7 +46,8 @@ import android.os.RemoteException; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; -import android.util.Log; +import android.util.Config; +import android.util.Slog; import android.util.SparseArray; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.IAccessibilityManager; @@ -57,6 +57,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -137,10 +138,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub registerPackageChangeAndBootCompletedBroadcastReceiver(); registerSettingsContentObservers(); - - synchronized (mLock) { - populateAccessibilityServiceListLocked(); - } } /** @@ -150,34 +147,83 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void registerPackageChangeAndBootCompletedBroadcastReceiver() { Context context = mContext; - BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + PackageMonitor monitor = new PackageMonitor() { @Override - public void onReceive(Context context, Intent intent) { + public void onSomePackagesChanged() { synchronized (mLock) { populateAccessibilityServiceListLocked(); manageServicesLocked(); + } + } + + @Override + public boolean onHandleForceStop(Intent intent, String[] packages, + int uid, boolean doit) { + synchronized (mLock) { + boolean changed = false; + Iterator<ComponentName> it = mEnabledServices.iterator(); + while (it.hasNext()) { + ComponentName comp = it.next(); + String compPkg = comp.getPackageName(); + for (String pkg : packages) { + if (compPkg.equals(pkg)) { + if (!doit) { + return true; + } + it.remove(); + changed = true; + } + } + } + if (changed) { + it = mEnabledServices.iterator(); + StringBuilder str = new StringBuilder(); + while (it.hasNext()) { + if (str.length() > 0) { + str.append(':'); + } + str.append(it.next().flattenToShortString()); + } + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + str.toString()); + manageServicesLocked(); + } + return false; + } + } + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { + synchronized (mLock) { + populateAccessibilityServiceListLocked(); - if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { + // get the accessibility enabled setting on boot mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; - updateClientsLocked(); + + // if accessibility is enabled inform our clients we are on + if (mIsEnabled) { + updateClientsLocked(); + } + + manageServicesLocked(); } + + return; } + + super.onReceive(context, intent); } }; // package changes - IntentFilter packageFilter = new IntentFilter(); - packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); - packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); - packageFilter.addDataScheme("package"); - context.registerReceiver(broadcastReceiver, packageFilter); + monitor.register(context, true); // boot completed IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); - mContext.registerReceiver(broadcastReceiver, bootFiler); + mContext.registerReceiver(monitor, bootFiler); } /** @@ -229,7 +275,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub client.setEnabled(mIsEnabled); mClients.add(client); } catch (RemoteException re) { - Log.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re); + Slog.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re); } } } @@ -263,13 +309,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub service.mServiceInterface.onInterrupt(); } catch (RemoteException re) { if (re instanceof DeadObjectException) { - Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up."); + Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up."); if (removeDeadServiceLocked(service)) { count--; i--; } } else { - Log.e(LOG_TAG, "Error during sending interrupt request to " + Slog.e(LOG_TAG, "Error during sending interrupt request to " + service.mService, re); } } @@ -297,7 +343,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } return; default: - Log.w(LOG_TAG, "Unknown message type: " + message.what); + Slog.w(LOG_TAG, "Unknown message type: " + message.what); } } @@ -404,17 +450,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub try { listener.onAccessibilityEvent(event); - if (LOGV) { - Log.i(LOG_TAG, "Event " + event + " sent to " + listener); + if (Config.DEBUG) { + Slog.i(LOG_TAG, "Event " + event + " sent to " + listener); } } catch (RemoteException re) { if (re instanceof DeadObjectException) { - Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up."); + Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up."); synchronized (mLock) { removeDeadServiceLocked(service); } } else { - Log.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re); + Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re); } } } @@ -429,8 +475,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mServices.remove(service); mHandler.removeMessages(service.mId); - if (LOGV) { - Log.i(LOG_TAG, "Dead service " + service.mService + " removed"); + if (Config.DEBUG) { + Slog.i(LOG_TAG, "Dead service " + service.mService + " removed"); } if (mServices.isEmpty()) { @@ -524,8 +570,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; splitter.setString(servicesValue); while (splitter.hasNext()) { - ComponentName enabledService = ComponentName.unflattenFromString(splitter.next()); - enabledServices.add(enabledService); + String str = splitter.next(); + if (str == null || str.length() <= 0) { + continue; + } + ComponentName enabledService = ComponentName.unflattenFromString(str); + if (enabledService != null) { + enabledServices.add(enabledService); + } } } } @@ -542,6 +594,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap; List<Service> services = mServices; + boolean isEnabled = mIsEnabled; for (int i = 0, count = installedServices.size(); i < count; i++) { ServiceInfo intalledService = installedServices.get(i); @@ -549,7 +602,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub intalledService.name); Service service = componentNameToServiceMap.get(componentName); - if (enabledServices.contains(componentName)) { + if (isEnabled && enabledServices.contains(componentName)) { if (service == null) { new Service(componentName).bind(); } @@ -573,6 +626,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } catch (RemoteException re) { mClients.remove(i); count--; + i--; } } } @@ -668,7 +722,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } } catch (RemoteException re) { - Log.w(LOG_TAG, "Error while setting Controller for service: " + service, re); + Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re); } } diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 8d86219..e088417 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -16,6 +16,7 @@ package com.android.server; +import android.app.Activity; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.IAlarmManager; @@ -36,7 +37,7 @@ import android.os.SystemProperties; import android.text.TextUtils; import android.text.format.Time; import android.util.EventLog; -import android.util.Log; +import android.util.Slog; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -127,8 +128,9 @@ class AlarmManagerService extends IAlarmManager.Stub { mTimeTickSender = PendingIntent.getBroadcast(context, 0, new Intent(Intent.ACTION_TIME_TICK).addFlags( Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0); - mDateChangeSender = PendingIntent.getBroadcast(context, 0, - new Intent(Intent.ACTION_DATE_CHANGED), 0); + Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0); // now that we have initied the driver schedule the alarm mClockReceiver= new ClockReceiver(); @@ -139,7 +141,7 @@ class AlarmManagerService extends IAlarmManager.Stub { if (mDescriptor != -1) { mWaitThread.start(); } else { - Log.w(TAG, "Failed to open alarm driver. Falling back to a handler."); + Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler."); } } @@ -158,7 +160,7 @@ class AlarmManagerService extends IAlarmManager.Stub { public void setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation) { if (operation == null) { - Log.w(TAG, "set/setRepeating ignored because there is no intent"); + Slog.w(TAG, "set/setRepeating ignored because there is no intent"); return; } synchronized (mLock) { @@ -171,7 +173,7 @@ class AlarmManagerService extends IAlarmManager.Stub { // Remove this alarm if already scheduled. removeLocked(operation); - if (localLOGV) Log.v(TAG, "set: " + alarm); + if (localLOGV) Slog.v(TAG, "set: " + alarm); int index = addAlarmLocked(alarm); if (index == 0) { @@ -183,7 +185,7 @@ class AlarmManagerService extends IAlarmManager.Stub { public void setInexactRepeating(int type, long triggerAtTime, long interval, PendingIntent operation) { if (operation == null) { - Log.w(TAG, "setInexactRepeating ignored because there is no intent"); + Slog.w(TAG, "setInexactRepeating ignored because there is no intent"); return; } @@ -235,12 +237,20 @@ class AlarmManagerService extends IAlarmManager.Stub { // Remember where this bucket started (reducing the amount of later // fixup required) and set the alarm with the new, bucketed start time. - if (localLOGV) Log.v(TAG, "setInexactRepeating: interval=" + interval + if (localLOGV) Slog.v(TAG, "setInexactRepeating: interval=" + interval + " bucketTime=" + bucketTime); mInexactDeliveryTimes[intervalSlot] = bucketTime; setRepeating(type, bucketTime, interval, operation); } + public void setTime(long millis) { + mContext.enforceCallingOrSelfPermission( + "android.permission.SET_TIME", + "setTime"); + + SystemClock.setCurrentTimeMillis(millis); + } + public void setTimeZone(String tz) { mContext.enforceCallingOrSelfPermission( "android.permission.SET_TIME_ZONE", @@ -254,7 +264,7 @@ class AlarmManagerService extends IAlarmManager.Stub { synchronized (this) { String current = SystemProperties.get(TIMEZONE_PROPERTY); if (current == null || !current.equals(zone.getID())) { - if (localLOGV) Log.v(TAG, "timezone changed: " + current + ", new=" + zone.getID()); + if (localLOGV) Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID()); timeZoneWasChanged = true; SystemProperties.set(TIMEZONE_PROPERTY, zone.getID()); } @@ -272,6 +282,7 @@ class AlarmManagerService extends IAlarmManager.Stub { if (timeZoneWasChanged) { Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra("time-zone", zone.getID()); mContext.sendBroadcast(intent); } @@ -334,6 +345,22 @@ class AlarmManagerService extends IAlarmManager.Stub { } } + public boolean lookForPackageLocked(String packageName) { + return lookForPackageLocked(mRtcWakeupAlarms, packageName) + || lookForPackageLocked(mRtcAlarms, packageName) + || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName) + || lookForPackageLocked(mElapsedRealtimeAlarms, packageName); + } + + private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) { + for (int i=alarmList.size()-1; i>=0; i--) { + if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) { + return true; + } + } + return false; + } + private ArrayList<Alarm> getAlarmList(int type) { switch (type) { case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms; @@ -352,18 +379,18 @@ class AlarmManagerService extends IAlarmManager.Stub { if (index < 0) { index = 0 - index - 1; } - if (localLOGV) Log.v(TAG, "Adding alarm " + alarm + " at " + index); + if (localLOGV) Slog.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); + Slog.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 + Slog.v(TAG, position + ": " + timeStr + " " + a.operation.getTargetPackage()); position += 1; } @@ -373,7 +400,7 @@ class AlarmManagerService extends IAlarmManager.Stub { } public long timeToNextAlarm() { - long nextAlarm = 0xfffffffffffffffl; + long nextAlarm = Long.MAX_VALUE; synchronized (mLock) { for (int i=AlarmManager.RTC_WAKEUP; i<=AlarmManager.ELAPSED_REALTIME; i++) { @@ -393,7 +420,18 @@ class AlarmManagerService extends IAlarmManager.Stub { { if (mDescriptor != -1) { - set(mDescriptor, alarm.type, (alarm.when * 1000 * 1000)); + // The kernel never triggers alarms with negative wakeup times + // so we ensure they are positive. + long alarmSeconds, alarmNanoseconds; + if (alarm.when < 0) { + alarmSeconds = 0; + alarmNanoseconds = 0; + } else { + alarmSeconds = alarm.when / 1000; + alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000; + } + + set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds); } else { @@ -472,7 +510,7 @@ class AlarmManagerService extends IAlarmManager.Stub { private native int init(); private native void close(int fd); - private native void set(int fd, int type, long nanoseconds); + private native void set(int fd, int type, long seconds, long nanoseconds); private native int waitForAlarm(int fd); private native int setKernelTimezone(int fd, int minuteswest); @@ -487,7 +525,7 @@ class AlarmManagerService extends IAlarmManager.Stub { { Alarm alarm = it.next(); - if (localLOGV) Log.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm); + if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm); if (alarm.when > now) { // don't fire alarms in the future @@ -499,14 +537,14 @@ class AlarmManagerService extends IAlarmManager.Stub { // 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 + Slog.v(TAG, "alarm is late! alarm time: " + alarm.when + " now: " + now + " delay (in seconds): " + (now - alarm.when) / 1000); } // 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); + if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm); alarm.count = 1; if (alarm.repeatInterval > 0) { // this adjustment will be zero if we're late by @@ -609,13 +647,15 @@ class AlarmManagerService extends IAlarmManager.Stub { if ((result & TIME_CHANGED_MASK) != 0) { remove(mTimeTickSender); mClockReceiver.scheduleTimeTickEvent(); - mContext.sendBroadcast(new Intent(Intent.ACTION_TIME_CHANGED)); + Intent intent = new Intent(Intent.ACTION_TIME_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + mContext.sendBroadcast(intent); } synchronized (mLock) { final long nowRTC = System.currentTimeMillis(); final long nowELAPSED = SystemClock.elapsedRealtime(); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Checking for alarms... rtc=" + nowRTC + ", elapsed=" + nowELAPSED); @@ -636,7 +676,7 @@ class AlarmManagerService extends IAlarmManager.Stub { while (it.hasNext()) { Alarm alarm = it.next(); try { - if (localLOGV) Log.v(TAG, "sending alarm " + alarm); + if (localLOGV) Slog.v(TAG, "sending alarm " + alarm); alarm.operation.send(mContext, 0, mBackgroundIntent.putExtra( Intent.EXTRA_ALARM_COUNT, alarm.count), @@ -667,7 +707,7 @@ class AlarmManagerService extends IAlarmManager.Stub { remove(alarm.operation); } } catch (RuntimeException e) { - Log.w(TAG, "Failure sending alarm.", e); + Slog.w(TAG, "Failure sending alarm.", e); } } } @@ -766,18 +806,50 @@ class AlarmManagerService extends IAlarmManager.Stub { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); + filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); filter.addDataScheme("package"); mContext.registerReceiver(this, filter); + // Register for events related to sdcard installation. + IntentFilter sdFilter = new IntentFilter(); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + mContext.registerReceiver(this, sdFilter); } @Override public void onReceive(Context context, Intent intent) { synchronized (mLock) { - Uri data = intent.getData(); - if (data != null) { - String pkg = data.getSchemeSpecificPart(); - removeLocked(pkg); - mBroadcastStats.remove(pkg); + String action = intent.getAction(); + String pkgList[] = null; + if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); + for (String packageName : pkgList) { + if (lookForPackageLocked(packageName)) { + setResultCode(Activity.RESULT_OK); + return; + } + } + return; + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + } else { + if (Intent.ACTION_PACKAGE_REMOVED.equals(action) + && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + // This package is being updated; don't kill its alarms. + return; + } + Uri data = intent.getData(); + if (data != null) { + String pkg = data.getSchemeSpecificPart(); + if (pkg != null) { + pkgList = new String[]{pkg}; + } + } + } + if (pkgList != null && (pkgList.length > 0)) { + for (String pkg : pkgList) { + removeLocked(pkg); + mBroadcastStats.remove(pkg); + } } } } diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index 6bf7102..24526af 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -29,6 +29,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.net.Uri; @@ -38,7 +39,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.util.AttributeSet; -import android.util.Log; +import android.util.Slog; import android.util.TypedValue; import android.util.Xml; import android.widget.RemoteViews; @@ -145,6 +146,11 @@ class AppWidgetService extends IAppWidgetService.Stub filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); mContext.registerReceiver(mBroadcastReceiver, filter); + // Register for events related to sdcard installation. + IntentFilter sdFilter = new IntentFilter(); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + mContext.registerReceiver(mBroadcastReceiver, sdFilter); } @Override @@ -423,7 +429,7 @@ class AppWidgetService extends IAppWidgetService.Stub synchronized (mAppWidgetIds) { Provider p = lookupProviderLocked(provider); if (p == null) { - Log.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider); + Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider); return; } ArrayList<AppWidgetId> instances = p.instances; @@ -518,10 +524,11 @@ class AppWidgetService extends IAppWidgetService.Stub } Provider lookupProviderLocked(ComponentName provider) { + final String className = provider.getClassName(); final int N = mInstalledProviders.size(); for (int i=0; i<N; i++) { Provider p = mInstalledProviders.get(i); - if (p.info.provider.equals(provider)) { + if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) { return p; } } @@ -568,7 +575,7 @@ class AppWidgetService extends IAppWidgetService.Stub List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); - final int N = broadcastReceivers.size(); + final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); for (int i=0; i<N; i++) { ResolveInfo ri = broadcastReceivers.get(i); addProviderLocked(ri); @@ -678,7 +685,7 @@ class AppWidgetService extends IAppWidgetService.Stub parser = activityInfo.loadXmlMetaData(mPackageManager, AppWidgetManager.META_DATA_APPWIDGET_PROVIDER); if (parser == null) { - Log.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for " + Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for " + "AppWidget provider '" + component + '\''); return null; } @@ -693,18 +700,25 @@ class AppWidgetService extends IAppWidgetService.Stub String nodeName = parser.getName(); if (!"appwidget-provider".equals(nodeName)) { - Log.w(TAG, "Meta-data does not start with appwidget-provider tag for" + Slog.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(); + // If metaData was null, we would have returned earlier when getting + // the parser No need to do the check here + info.oldName = activityInfo.metaData.getString( + AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME); info.provider = component; p.uid = activityInfo.applicationInfo.uid; - TypedArray sa = mContext.getResources().obtainAttributes(attrs, + Resources res = mPackageManager.getResourcesForApplication( + activityInfo.applicationInfo); + + TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AppWidgetProviderInfo); // These dimensions has to be resolved in the application's context. @@ -732,7 +746,7 @@ class AppWidgetService extends IAppWidgetService.Stub // 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); + Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e); return null; } finally { if (parser != null) parser.close(); @@ -824,7 +838,7 @@ class AppWidgetService extends IAppWidgetService.Stub } if (!writeStateToFileLocked(temp)) { - Log.w(TAG, "Failed to persist new settings"); + Slog.w(TAG, "Failed to persist new settings"); return; } @@ -926,6 +940,16 @@ class AppWidgetService extends IAppWidgetService.Stub // as before? String pkg = parser.getAttributeValue(null, "pkg"); String cl = parser.getAttributeValue(null, "cl"); + + final PackageManager packageManager = mContext.getPackageManager(); + try { + packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0); + } catch (PackageManager.NameNotFoundException e) { + String[] pkgs = packageManager.currentToCanonicalPackageNames( + new String[] { pkg }); + pkg = pkgs[0]; + } + Provider p = lookupProviderLocked(new ComponentName(pkg, cl)); if (p == null && mSafeMode) { // if we're in safe mode, make a temporary one @@ -975,7 +999,7 @@ class AppWidgetService extends IAppWidgetService.Stub int pIndex = Integer.parseInt(providerString, 16); id.provider = loadedProviders.get(pIndex); if (false) { - Log.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider " + Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider " + pIndex + " which is " + id.provider); } if (id.provider == null) { @@ -1002,15 +1026,15 @@ class AppWidgetService extends IAppWidgetService.Stub } while (type != XmlPullParser.END_DOCUMENT); success = true; } catch (NullPointerException e) { - Log.w(TAG, "failed parsing " + file, e); + Slog.w(TAG, "failed parsing " + file, e); } catch (NumberFormatException e) { - Log.w(TAG, "failed parsing " + file, e); + Slog.w(TAG, "failed parsing " + file, e); } catch (XmlPullParserException e) { - Log.w(TAG, "failed parsing " + file, e); + Slog.w(TAG, "failed parsing " + file, e); } catch (IOException e) { - Log.w(TAG, "failed parsing " + file, e); + Slog.w(TAG, "failed parsing " + file, e); } catch (IndexOutOfBoundsException e) { - Log.w(TAG, "failed parsing " + file, e); + Slog.w(TAG, "failed parsing " + file, e); } try { if (stream != null) { @@ -1050,7 +1074,7 @@ class AppWidgetService extends IAppWidgetService.Stub BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - //Log.d(TAG, "received " + action); + //Slog.d(TAG, "received " + action); if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { sendInitialBroadcasts(); } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { @@ -1070,36 +1094,55 @@ class AppWidgetService extends IAppWidgetService.Stub } } } else { - Uri uri = intent.getData(); - if (uri == null) { - return; + boolean added = false; + String pkgList[] = null; + if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + added = true; + } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + added = false; + } else { + Uri uri = intent.getData(); + if (uri == null) { + return; + } + String pkgName = uri.getSchemeSpecificPart(); + if (pkgName == null) { + return; + } + pkgList = new String[] { pkgName }; + added = Intent.ACTION_PACKAGE_ADDED.equals(action); } - String pkgName = uri.getSchemeSpecificPart(); - if (pkgName == null) { + if (pkgList == null || pkgList.length == 0) { return; } - - if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + if (added) { synchronized (mAppWidgetIds) { Bundle extras = intent.getExtras(); if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { - // The package was just upgraded - updateProvidersForPackageLocked(pkgName); + for (String pkgName : pkgList) { + // The package was just upgraded + updateProvidersForPackageLocked(pkgName); + } } else { // The package was just added - addProvidersForPackageLocked(pkgName); + for (String pkgName : pkgList) { + addProvidersForPackageLocked(pkgName); + } } saveStateLocked(); } - } - else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + } else { 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(); + for (String pkgName : pkgList) { + removeProvidersForPackageLocked(pkgName); + saveStateLocked(); + } } } } @@ -1107,14 +1150,13 @@ class AppWidgetService extends IAppWidgetService.Stub } }; - // 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); + intent.setPackage(pkgName); List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); - final int N = broadcastReceivers.size(); + final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); for (int i=0; i<N; i++) { ResolveInfo ri = broadcastReceivers.get(i); ActivityInfo ai = ri.activityInfo; @@ -1125,16 +1167,15 @@ class AppWidgetService extends IAppWidgetService.Stub } } - // 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<String>(); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + intent.setPackage(pkgName); List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); // add the missing ones and collect which ones to keep - int N = broadcastReceivers.size(); + int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); for (int i=0; i<N; i++) { ResolveInfo ri = broadcastReceivers.get(i); ActivityInfo ai = ri.activityInfo; diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index c3b591e..d67dde0 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -17,11 +17,16 @@ package com.android.server; import android.app.ActivityManagerNative; +import android.app.ActivityThread; import android.app.AlarmManager; import android.app.IActivityManager; import android.app.IApplicationThread; import android.app.IBackupAgent; import android.app.PendingIntent; +import android.app.backup.RestoreSet; +import android.app.backup.IBackupManager; +import android.app.backup.IRestoreObserver; +import android.app.backup.IRestoreSession; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -30,53 +35,51 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageDataObserver; +import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager; import android.content.pm.Signature; +import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; -import android.provider.Settings; import android.os.Binder; import android.os.Bundle; import android.os.Environment; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; +import android.provider.Settings; import android.util.EventLog; -import android.util.Log; +import android.util.Slog; import android.util.SparseArray; - -import android.backup.IBackupManager; -import android.backup.IRestoreObserver; -import android.backup.IRestoreSession; -import android.backup.RestoreSet; +import android.util.SparseIntArray; import com.android.internal.backup.BackupConstants; -import com.android.internal.backup.LocalTransport; import com.android.internal.backup.IBackupTransport; - -import com.android.server.PackageManagerBackupAgent; +import com.android.internal.backup.LocalTransport; import com.android.server.PackageManagerBackupAgent.Metadata; import java.io.EOFException; import java.io.File; import java.io.FileDescriptor; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.RandomAccessFile; -import java.lang.String; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "BackupManagerService"; @@ -93,44 +96,38 @@ class BackupManagerService extends IBackupManager.Stub { // the first backup pass. private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR; - private static final String RUN_BACKUP_ACTION = "android.backup.intent.RUN"; - private static final String RUN_INITIALIZE_ACTION = "android.backup.intent.INIT"; - private static final String RUN_CLEAR_ACTION = "android.backup.intent.CLEAR"; + private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN"; + private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT"; + private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR"; private static final int MSG_RUN_BACKUP = 1; private static final int MSG_RUN_FULL_BACKUP = 2; private static final int MSG_RUN_RESTORE = 3; private static final int MSG_RUN_CLEAR = 4; private static final int MSG_RUN_INITIALIZE = 5; - - // Event tags -- see system/core/logcat/event-log-tags - private static final int BACKUP_DATA_CHANGED_EVENT = 2820; - private static final int BACKUP_START_EVENT = 2821; - private static final int BACKUP_TRANSPORT_FAILURE_EVENT = 2822; - private static final int BACKUP_AGENT_FAILURE_EVENT = 2823; - private static final int BACKUP_PACKAGE_EVENT = 2824; - private static final int BACKUP_SUCCESS_EVENT = 2825; - private static final int BACKUP_RESET_EVENT = 2826; - private static final int BACKUP_INITIALIZE_EVENT = 2827; - - private static final int RESTORE_START_EVENT = 2830; - private static final int RESTORE_TRANSPORT_FAILURE_EVENT = 2831; - private static final int RESTORE_AGENT_FAILURE_EVENT = 2832; - private static final int RESTORE_PACKAGE_EVENT = 2833; - private static final int RESTORE_SUCCESS_EVENT = 2834; + private static final int MSG_RUN_GET_RESTORE_SETS = 6; + private static final int MSG_TIMEOUT = 7; // Timeout interval for deciding that a bind or clear-data has taken too long static final long TIMEOUT_INTERVAL = 10 * 1000; + // Timeout intervals for agent backup & restore operations + static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000; + static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000; + private Context mContext; private PackageManager mPackageManager; + IPackageManager mPackageManagerBinder; private IActivityManager mActivityManager; private PowerManager mPowerManager; private AlarmManager mAlarmManager; + IBackupManager mBackupManagerBinder; boolean mEnabled; // access to this is synchronized on 'this' boolean mProvisioned; + boolean mAutoRestore; PowerManager.WakeLock mWakelock; - final BackupHandler mBackupHandler = new BackupHandler(); + HandlerThread mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND); + BackupHandler mBackupHandler; PendingIntent mRunBackupIntent, mRunInitIntent; BroadcastReceiver mRunBackupReceiver, mRunInitReceiver; // map UIDs to the set of backup client services within that UID's app set @@ -167,7 +164,6 @@ class BackupManagerService extends IBackupManager.Stub { final Object mAgentConnectLock = new Object(); IBackupAgent mConnectedAgent; volatile boolean mConnecting; - volatile boolean mBackupOrRestoreInProgress = false; volatile long mLastBackupPass; volatile long mNextBackupPass; @@ -180,17 +176,43 @@ class BackupManagerService extends IBackupManager.Stub { = new HashMap<String,IBackupTransport>(); String mCurrentTransport; IBackupTransport mLocalTransport, mGoogleTransport; - RestoreSession mActiveRestoreSession; + ActiveRestoreSession mActiveRestoreSession; + + class RestoreGetSetsParams { + public IBackupTransport transport; + public ActiveRestoreSession session; + public IRestoreObserver observer; + + RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session, + IRestoreObserver _observer) { + transport = _transport; + session = _session; + observer = _observer; + } + } class RestoreParams { public IBackupTransport transport; public IRestoreObserver observer; public long token; + public PackageInfo pkgInfo; + public int pmToken; // in post-install restore, the PM's token for this transaction + + RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, + long _token, PackageInfo _pkg, int _pmToken) { + transport = _transport; + observer = _obs; + token = _token; + pkgInfo = _pkg; + pmToken = _pmToken; + } RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) { transport = _transport; observer = _obs; token = _token; + pkgInfo = null; + pmToken = 0; } } @@ -204,35 +226,203 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Bookkeeping of in-flight operations for timeout etc. purposes. The operation + // token is the index of the entry in the pending-operations list. + static final int OP_PENDING = 0; + static final int OP_ACKNOWLEDGED = 1; + static final int OP_TIMEOUT = -1; + + final SparseIntArray mCurrentOperations = new SparseIntArray(); + final Object mCurrentOpLock = new Object(); + final Random mTokenGenerator = new Random(); + // Where we keep our journal files and other bookkeeping File mBaseStateDir; File mDataDir; File mJournalDir; File mJournal; - // Keep a log of all the apps we've ever backed up + // Keep a log of all the apps we've ever backed up, and what the + // dataset tokens are for both the current backup dataset and + // the ancestral dataset. private File mEverStored; HashSet<String> mEverStoredApps = new HashSet<String>(); + static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes + File mTokenFile; + Set<String> mAncestralPackages = null; + long mAncestralToken = 0; + long mCurrentToken = 0; + // Persistently track the need to do a full init static final String INIT_SENTINEL_FILE_NAME = "_need_init_"; HashSet<String> mPendingInits = new HashSet<String>(); // transport names - volatile boolean mInitInProgress = false; + + // ----- Asynchronous backup/restore handler thread ----- + + private class BackupHandler extends Handler { + public BackupHandler(Looper looper) { + super(looper); + } + + public void handleMessage(Message msg) { + + switch (msg.what) { + case MSG_RUN_BACKUP: + { + mLastBackupPass = System.currentTimeMillis(); + mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL; + + IBackupTransport transport = getTransport(mCurrentTransport); + if (transport == null) { + Slog.v(TAG, "Backup requested but no transport available"); + mWakelock.release(); + break; + } + + // snapshot the pending-backup set and work on that + ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>(); + File oldJournal = mJournal; + synchronized (mQueueLock) { + // Do we have any work to do? Construct the work queue + // then release the synchronization lock to actually run + // the backup. + if (mPendingBackups.size() > 0) { + for (BackupRequest b: mPendingBackups.values()) { + queue.add(b); + } + if (DEBUG) Slog.v(TAG, "clearing pending backups"); + mPendingBackups.clear(); + + // Start a new backup-queue journal file too + mJournal = null; + + } + } + + if (queue.size() > 0) { + // At this point, we have started a new journal file, and the old + // file identity is being passed to the backup processing thread. + // When it completes successfully, that old journal file will be + // deleted. If we crash prior to that, the old journal is parsed + // at next boot and the journaled requests fulfilled. + (new PerformBackupTask(transport, queue, oldJournal)).run(); + } else { + Slog.v(TAG, "Backup requested but nothing pending"); + mWakelock.release(); + } + break; + } + + case MSG_RUN_FULL_BACKUP: + break; + + case MSG_RUN_RESTORE: + { + RestoreParams params = (RestoreParams)msg.obj; + Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); + (new PerformRestoreTask(params.transport, params.observer, + params.token, params.pkgInfo, params.pmToken)).run(); + break; + } + + case MSG_RUN_CLEAR: + { + ClearParams params = (ClearParams)msg.obj; + (new PerformClearTask(params.transport, params.packageInfo)).run(); + break; + } + + case MSG_RUN_INITIALIZE: + { + HashSet<String> queue; + + // Snapshot the pending-init queue and work on that + synchronized (mQueueLock) { + queue = new HashSet<String>(mPendingInits); + mPendingInits.clear(); + } + + (new PerformInitializeTask(queue)).run(); + break; + } + + case MSG_RUN_GET_RESTORE_SETS: + { + // Like other async operations, this is entered with the wakelock held + RestoreSet[] sets = null; + RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj; + try { + sets = params.transport.getAvailableRestoreSets(); + // cache the result in the active session + synchronized (params.session) { + params.session.mRestoreSets = sets; + } + if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); + } catch (Exception e) { + Slog.e(TAG, "Error from transport getting set list"); + } finally { + if (params.observer != null) { + try { + params.observer.restoreSetsAvailable(sets); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to report listing to observer"); + } catch (Exception e) { + Slog.e(TAG, "Restore observer threw", e); + } + } + + mWakelock.release(); + } + break; + } + + case MSG_TIMEOUT: + { + synchronized (mCurrentOpLock) { + final int token = msg.arg1; + int state = mCurrentOperations.get(token, OP_TIMEOUT); + if (state == OP_PENDING) { + if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + token); + mCurrentOperations.put(token, OP_TIMEOUT); + } + mCurrentOpLock.notifyAll(); + } + break; + } + } + } + } + + // ----- Main service implementation ----- public BackupManagerService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); + mPackageManagerBinder = ActivityThread.getPackageManager(); mActivityManager = ActivityManagerNative.getDefault(); mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mBackupManagerBinder = asInterface(asBinder()); + + // spin up the backup/restore handler thread + mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND); + mHandlerThread.start(); + mBackupHandler = new BackupHandler(mHandlerThread.getLooper()); + // Set up our bookkeeping boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.BACKUP_ENABLED, 0) != 0; mProvisioned = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.BACKUP_PROVISIONED, 0) != 0; + mAutoRestore = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0; + // If Encrypted file systems is enabled or disabled, this call will return the + // correct directory. mBaseStateDir = new File(Environment.getDataDirectory(), "backup"); + mBaseStateDir.mkdirs(); mDataDir = Environment.getDownloadCacheDirectory(); // Alarm receivers for scheduled backups & initialization operations @@ -284,15 +474,30 @@ class BackupManagerService extends IBackupManager.Stub { if ("".equals(mCurrentTransport)) { mCurrentTransport = null; } - if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport); + if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport); // Attach to the Google backup transport. When this comes up, it will set // itself as the current transport because we explicitly reset mCurrentTransport // to null. - Intent intent = new Intent().setComponent(new ComponentName( - "com.google.android.backup", - "com.google.android.backup.BackupTransportService")); - context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE); + ComponentName transportComponent = new ComponentName("com.google.android.backup", + "com.google.android.backup.BackupTransportService"); + try { + // If there's something out there that is supposed to be the Google + // backup transport, make sure it's legitimately part of the OS build + // and not an app lying about its package name. + ApplicationInfo info = mPackageManager.getApplicationInfo( + transportComponent.getPackageName(), 0); + if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + if (DEBUG) Slog.v(TAG, "Binding to Google transport"); + Intent intent = new Intent().setComponent(transportComponent); + context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE); + } else { + Slog.w(TAG, "Possible Google transport spoof: ignoring " + info); + } + } catch (PackageManager.NameNotFoundException nnf) { + // No such package? No binding. + if (DEBUG) Slog.v(TAG, "Google transport not present"); + } // Now that we know about valid backup participants, parse any // leftover journal files into the pending backup set @@ -312,22 +517,19 @@ class BackupManagerService extends IBackupManager.Stub { if (mPendingInits.size() > 0) { // If there are pending init operations, we process those // and then settle into the usual periodic backup schedule. - if (DEBUG) Log.v(TAG, "Init pending at scheduled backup"); + if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup"); try { mAlarmManager.cancel(mRunInitIntent); mRunInitIntent.send(); } catch (PendingIntent.CanceledException ce) { - Log.e(TAG, "Run init intent cancelled"); + Slog.e(TAG, "Run init intent cancelled"); // can't really do more than bail here } } else { - // Don't run backups now if we're disabled, not yet - // fully set up, in the middle of a backup already, - // or racing with an initialize pass. - if (mEnabled && mProvisioned - && !mBackupOrRestoreInProgress && !mInitInProgress) { - if (DEBUG) Log.v(TAG, "Running a backup pass"); - mBackupOrRestoreInProgress = true; + // Don't run backups now if we're disabled or not yet + // fully set up. + if (mEnabled && mProvisioned) { + if (DEBUG) Slog.v(TAG, "Running a backup pass"); // Acquire the wakelock and pass it to the backup thread. it will // be released once backup concludes. @@ -336,8 +538,7 @@ class BackupManagerService extends IBackupManager.Stub { Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP); mBackupHandler.sendMessage(msg); } else { - Log.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned - + " b=" + mBackupOrRestoreInProgress + " i=" + mInitInProgress); + Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned); } } } @@ -349,8 +550,7 @@ class BackupManagerService extends IBackupManager.Stub { public void onReceive(Context context, Intent intent) { if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) { synchronized (mQueueLock) { - if (DEBUG) Log.v(TAG, "Running a device init"); - mInitInProgress = true; + if (DEBUG) Slog.v(TAG, "Running a device init"); // Acquire the wakelock and pass it to the init thread. it will // be released once init concludes. @@ -364,7 +564,32 @@ class BackupManagerService extends IBackupManager.Stub { } private void initPackageTracking() { - if (DEBUG) Log.v(TAG, "Initializing package tracking"); + if (DEBUG) Slog.v(TAG, "Initializing package tracking"); + + // Remember our ancestral dataset + mTokenFile = new File(mBaseStateDir, "ancestral"); + try { + RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r"); + int version = tf.readInt(); + if (version == CURRENT_ANCESTRAL_RECORD_VERSION) { + mAncestralToken = tf.readLong(); + mCurrentToken = tf.readLong(); + + int numPackages = tf.readInt(); + if (numPackages >= 0) { + mAncestralPackages = new HashSet<String>(); + for (int i = 0; i < numPackages; i++) { + String pkgName = tf.readUTF(); + mAncestralPackages.add(pkgName); + } + } + } + } catch (FileNotFoundException fnf) { + // Probably innocuous + Slog.v(TAG, "No ancestral data"); + } catch (IOException e) { + Slog.w(TAG, "Unable to read token file", e); + } // Keep a log of what apps we've ever backed up. Because we might have // rebooted in the middle of an operation that was removing something from @@ -396,20 +621,20 @@ class BackupManagerService extends IBackupManager.Stub { info = mPackageManager.getPackageInfo(pkg, 0); mEverStoredApps.add(pkg); temp.writeUTF(pkg); - if (DEBUG) Log.v(TAG, " + " + pkg); + if (DEBUG) Slog.v(TAG, " + " + pkg); } catch (NameNotFoundException e) { // nope, this package was uninstalled; don't include it - if (DEBUG) Log.v(TAG, " - " + pkg); + if (DEBUG) Slog.v(TAG, " - " + pkg); } } } catch (EOFException e) { // Once we've rewritten the backup history log, atomically replace the // old one with the new one then reopen the file for continuing use. if (!tempProcessedFile.renameTo(mEverStored)) { - Log.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored); + Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored); } } catch (IOException e) { - Log.e(TAG, "Error in processed file", e); + Slog.e(TAG, "Error in processed file", e); } finally { try { if (temp != null) temp.close(); } catch (IOException e) {} try { if (in != null) in.close(); } catch (IOException e) {} @@ -423,6 +648,11 @@ class BackupManagerService extends IBackupManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); mContext.registerReceiver(mBroadcastReceiver, filter); + // Register for events related to sdcard installation. + IntentFilter sdFilter = new IntentFilter(); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + mContext.registerReceiver(mBroadcastReceiver, sdFilter); } private void parseLeftoverJournals() { @@ -433,17 +663,17 @@ class BackupManagerService extends IBackupManager.Stub { // backup. RandomAccessFile in = null; try { - Log.i(TAG, "Found stale backup journal, scheduling:"); + Slog.i(TAG, "Found stale backup journal, scheduling:"); in = new RandomAccessFile(f, "r"); while (true) { String packageName = in.readUTF(); - Log.i(TAG, " + " + packageName); + Slog.i(TAG, " + " + packageName); dataChanged(packageName); } } catch (EOFException e) { // no more data; we're done } catch (Exception e) { - Log.e(TAG, "Can't read " + f, e); + Slog.e(TAG, "Can't read " + f, e); } finally { // close/delete the file try { if (in != null) in.close(); } catch (IOException e) {} @@ -456,7 +686,7 @@ class BackupManagerService extends IBackupManager.Stub { // Maintain persistent state around whether need to do an initialize operation. // Must be called with the queue lock held. void recordInitPendingLocked(boolean isPending, String transportName) { - if (DEBUG) Log.i(TAG, "recordInitPendingLocked: " + isPending + if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending + " on transport " + transportName); try { IBackupTransport transport = getTransport(transportName); @@ -493,6 +723,9 @@ class BackupManagerService extends IBackupManager.Stub { mEverStoredApps.clear(); mEverStored.delete(); + mCurrentToken = 0; + writeRestoreTokens(); + // Remove all the state files for (File sf : stateFileDir.listFiles()) { // ... but don't touch the needs-init sentinel @@ -513,11 +746,21 @@ class BackupManagerService extends IBackupManager.Stub { } } - // Add a transport to our set of available backends + // Add a transport to our set of available backends. If 'transport' is null, this + // is an unregistration, and the transport's entry is removed from our bookkeeping. private void registerTransport(String name, IBackupTransport transport) { synchronized (mTransports) { - if (DEBUG) Log.v(TAG, "Registering transport " + name + " = " + transport); - mTransports.put(name, transport); + if (DEBUG) Slog.v(TAG, "Registering transport " + name + " = " + transport); + if (transport != null) { + mTransports.put(name, transport); + } else { + mTransports.remove(name); + if ((mCurrentTransport != null) && mCurrentTransport.equals(name)) { + mCurrentTransport = null; + } + // Nothing further to do in the unregistration case + return; + } } // If the init sentinel file exists, we need to be sure to perform the init @@ -547,37 +790,55 @@ class BackupManagerService extends IBackupManager.Stub { // ----- Track installation/removal of packages ----- BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { - if (DEBUG) Log.d(TAG, "Received broadcast " + intent); + if (DEBUG) Slog.d(TAG, "Received broadcast " + intent); - Uri uri = intent.getData(); - if (uri == null) { - return; + String action = intent.getAction(); + boolean replacing = false; + boolean added = false; + Bundle extras = intent.getExtras(); + String pkgList[] = null; + if (Intent.ACTION_PACKAGE_ADDED.equals(action) || + Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + Uri uri = intent.getData(); + if (uri == null) { + return; + } + String pkgName = uri.getSchemeSpecificPart(); + if (pkgName != null) { + pkgList = new String[] { pkgName }; + } + added = Intent.ACTION_PACKAGE_ADDED.equals(action); + replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false); + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { + added = true; + pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + added = false; + pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } - String pkgName = uri.getSchemeSpecificPart(); - if (pkgName == null) { + if (pkgList == null || pkgList.length == 0) { return; } - - String action = intent.getAction(); - if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + if (added) { synchronized (mBackupParticipants) { - Bundle extras = intent.getExtras(); - if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { - // The package was just upgraded - updatePackageParticipantsLocked(pkgName); - } else { - // The package was just added - addPackageParticipantsLocked(pkgName); + for (String pkgName : pkgList) { + if (replacing) { + // The package was just upgraded + updatePackageParticipantsLocked(pkgName); + } else { + // The package was just added + addPackageParticipantsLocked(pkgName); + } } } - } - else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - Bundle extras = intent.getExtras(); - if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { + } else { + if (replacing) { // The package is being updated. We'll receive a PACKAGE_ADDED shortly. } else { synchronized (mBackupParticipants) { - removePackageParticipantsLocked(pkgName); + for (String pkgName : pkgList) { + removePackageParticipantsLocked(pkgName); + } } } } @@ -587,112 +848,23 @@ class BackupManagerService extends IBackupManager.Stub { // ----- Track connection to GoogleBackupTransport service ----- ServiceConnection mGoogleConnection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) Log.v(TAG, "Connected to Google transport"); + if (DEBUG) Slog.v(TAG, "Connected to Google transport"); mGoogleTransport = IBackupTransport.Stub.asInterface(service); registerTransport(name.flattenToShortString(), mGoogleTransport); } public void onServiceDisconnected(ComponentName name) { - if (DEBUG) Log.v(TAG, "Disconnected from Google transport"); + if (DEBUG) Slog.v(TAG, "Disconnected from Google transport"); mGoogleTransport = null; registerTransport(name.flattenToShortString(), null); } }; - // ----- Run the actual backup process asynchronously ----- - - private class BackupHandler extends Handler { - public void handleMessage(Message msg) { - - switch (msg.what) { - case MSG_RUN_BACKUP: - { - mLastBackupPass = System.currentTimeMillis(); - mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL; - - IBackupTransport transport = getTransport(mCurrentTransport); - if (transport == null) { - Log.v(TAG, "Backup requested but no transport available"); - synchronized (mQueueLock) { - mBackupOrRestoreInProgress = false; - } - mWakelock.release(); - break; - } - - // snapshot the pending-backup set and work on that - ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>(); - synchronized (mQueueLock) { - // Do we have any work to do? - if (mPendingBackups.size() > 0) { - for (BackupRequest b: mPendingBackups.values()) { - queue.add(b); - } - Log.v(TAG, "clearing pending backups"); - mPendingBackups.clear(); - - // Start a new backup-queue journal file too - File oldJournal = mJournal; - mJournal = null; - - // At this point, we have started a new journal file, and the old - // file identity is being passed to the backup processing thread. - // When it completes successfully, that old journal file will be - // deleted. If we crash prior to that, the old journal is parsed - // at next boot and the journaled requests fulfilled. - (new PerformBackupThread(transport, queue, oldJournal)).start(); - } else { - Log.v(TAG, "Backup requested but nothing pending"); - synchronized (mQueueLock) { - mBackupOrRestoreInProgress = false; - } - mWakelock.release(); - } - } - break; - } - - case MSG_RUN_FULL_BACKUP: - break; - - case MSG_RUN_RESTORE: - { - RestoreParams params = (RestoreParams)msg.obj; - Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); - (new PerformRestoreThread(params.transport, params.observer, - params.token)).start(); - break; - } - - case MSG_RUN_CLEAR: - { - ClearParams params = (ClearParams)msg.obj; - (new PerformClearThread(params.transport, params.packageInfo)).start(); - break; - } - - case MSG_RUN_INITIALIZE: - { - HashSet<String> queue; - - // Snapshot the pending-init queue and work on that - synchronized (mQueueLock) { - queue = new HashSet<String>(mPendingInits); - mPendingInits.clear(); - } - - (new PerformInitializeThread(queue)).start(); - break; - } - } - } - } - // Add the backup agents in the given package to our set of known backup participants. // If 'packageName' is null, adds all backup agents in the whole system. void addPackageParticipantsLocked(String packageName) { // Look for apps that define the android:backupAgent attribute - if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName); + if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: " + packageName); List<PackageInfo> targetApps = allAgentPackages(); addPackageParticipantsLockedInner(packageName, targetApps); } @@ -700,14 +872,12 @@ class BackupManagerService extends IBackupManager.Stub { private void addPackageParticipantsLockedInner(String packageName, List<PackageInfo> targetPkgs) { if (DEBUG) { - Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:"); + Slog.v(TAG, "Adding " + targetPkgs.size() + " backup participants:"); for (PackageInfo p : targetPkgs) { - Log.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName + Slog.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName + " uid=" + p.applicationInfo.uid + " killAfterRestore=" + (((p.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) ? "true" : "false") - + " restoreNeedsApplication=" - + (((p.applicationInfo.flags & ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION) != 0) ? "true" : "false") ); } } @@ -724,7 +894,7 @@ class BackupManagerService extends IBackupManager.Stub { // If we've never seen this app before, schedule a backup for it if (!mEverStoredApps.contains(pkg.packageName)) { - if (DEBUG) Log.i(TAG, "New app " + pkg.packageName + if (DEBUG) Slog.i(TAG, "New app " + pkg.packageName + " never backed up; scheduling"); dataChanged(pkg.packageName); } @@ -735,7 +905,7 @@ class BackupManagerService extends IBackupManager.Stub { // Remove the given package's entry from our known active set. If // 'packageName' is null, *all* participating apps will be removed. void removePackageParticipantsLocked(String packageName) { - if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName); + if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: " + packageName); List<PackageInfo> allApps = null; if (packageName != null) { allApps = new ArrayList<PackageInfo>(); @@ -755,10 +925,10 @@ class BackupManagerService extends IBackupManager.Stub { private void removePackageParticipantsLockedInner(String packageName, List<PackageInfo> agents) { if (DEBUG) { - Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName + Slog.v(TAG, "removePackageParticipantsLockedInner (" + packageName + ") removing " + agents.size() + " entries"); for (PackageInfo p : agents) { - Log.v(TAG, " - " + p); + Slog.v(TAG, " - " + p); } } for (PackageInfo pkg : agents) { @@ -794,9 +964,7 @@ class BackupManagerService extends IBackupManager.Stub { try { ApplicationInfo app = pkg.applicationInfo; if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) - || app.backupAgentName == null - || (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA, - pkg.packageName) != PackageManager.PERMISSION_GRANTED)) { + || app.backupAgentName == null) { packages.remove(a); } else { @@ -816,10 +984,10 @@ class BackupManagerService extends IBackupManager.Stub { // action cannot be passed a null package name. void updatePackageParticipantsLocked(String packageName) { if (packageName == null) { - Log.e(TAG, "updatePackageParticipants called with null package name"); + Slog.e(TAG, "updatePackageParticipants called with null package name"); return; } - if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName); + if (DEBUG) Slog.v(TAG, "updatePackageParticipantsLocked: " + packageName); // brute force but small code size List<PackageInfo> allApps = allAgentPackages(); @@ -827,7 +995,7 @@ class BackupManagerService extends IBackupManager.Stub { addPackageParticipantsLockedInner(packageName, allApps); } - // Called from the backup thread: record that the given app has been successfully + // Called from the backup task: record that the given app has been successfully // backed up at least once void logBackupComplete(String packageName) { if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return; @@ -841,7 +1009,7 @@ class BackupManagerService extends IBackupManager.Stub { out.seek(out.length()); out.writeUTF(packageName); } catch (IOException e) { - Log.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored); + Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored); } finally { try { if (out != null) out.close(); } catch (IOException e) {} } @@ -850,7 +1018,7 @@ class BackupManagerService extends IBackupManager.Stub { // Remove our awareness of having ever backed up the given package void removeEverBackedUp(String packageName) { - if (DEBUG) Log.v(TAG, "Removing backed-up knowledge of " + packageName + ", new set:"); + if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName + ", new set:"); synchronized (mEverStoredApps) { // Rewrite the file and rename to overwrite. If we reboot in the middle, @@ -863,7 +1031,7 @@ class BackupManagerService extends IBackupManager.Stub { mEverStoredApps.remove(packageName); for (String s : mEverStoredApps) { known.writeUTF(s); - if (DEBUG) Log.v(TAG, " " + s); + if (DEBUG) Slog.v(TAG, " " + s); } known.close(); known = null; @@ -875,7 +1043,7 @@ class BackupManagerService extends IBackupManager.Stub { // abandon the whole process and remove all what's-backed-up // state entirely, meaning we'll force a backup pass for every // participant on the next boot or [re]install. - Log.w(TAG, "Error rewriting " + mEverStored, e); + Slog.w(TAG, "Error rewriting " + mEverStored, e); mEverStoredApps.clear(); tempKnownFile.delete(); mEverStored.delete(); @@ -885,12 +1053,43 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Persistently record the current and ancestral backup tokens as well + // as the set of packages with data [supposedly] available in the + // ancestral dataset. + void writeRestoreTokens() { + try { + RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd"); + + // First, the version number of this record, for futureproofing + af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION); + + // Write the ancestral and current tokens + af.writeLong(mAncestralToken); + af.writeLong(mCurrentToken); + + // Now write the set of ancestral packages + if (mAncestralPackages == null) { + af.writeInt(-1); + } else { + af.writeInt(mAncestralPackages.size()); + if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size()); + for (String pkgName : mAncestralPackages) { + af.writeUTF(pkgName); + if (DEBUG) Slog.v(TAG, " " + pkgName); + } + } + af.close(); + } catch (IOException e) { + Slog.w(TAG, "Unable to write token file:", e); + } + } + // Return the given transport private IBackupTransport getTransport(String transportName) { synchronized (mTransports) { IBackupTransport transport = mTransports.get(transportName); if (transport == null) { - Log.w(TAG, "Requested unavailable transport: " + transportName); + Slog.w(TAG, "Requested unavailable transport: " + transportName); } return transport; } @@ -904,7 +1103,7 @@ class BackupManagerService extends IBackupManager.Stub { mConnectedAgent = null; try { if (mActivityManager.bindBackupAgent(app, mode)) { - Log.d(TAG, "awaiting agent for " + app); + Slog.d(TAG, "awaiting agent for " + app); // success; wait for the agent to arrive // only wait 10 seconds for the clear data to happen @@ -921,7 +1120,7 @@ class BackupManagerService extends IBackupManager.Stub { // if we timed out with no connect, abort and move on if (mConnecting == true) { - Log.w(TAG, "Timeout waiting for agent " + app); + Slog.w(TAG, "Timeout waiting for agent " + app); return null; } agent = mConnectedAgent; @@ -939,12 +1138,12 @@ class BackupManagerService extends IBackupManager.Stub { try { PackageInfo info = mPackageManager.getPackageInfo(packageName, 0); if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) { - if (DEBUG) Log.i(TAG, "allowClearUserData=false so not wiping " + if (DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping " + packageName); return; } } catch (NameNotFoundException e) { - Log.w(TAG, "Tried to clear data for " + packageName + " but not found"); + Slog.w(TAG, "Tried to clear data for " + packageName + " but not found"); return; } @@ -952,15 +1151,11 @@ class BackupManagerService extends IBackupManager.Stub { synchronized(mClearDataLock) { mClearingData = true; - /* This is causing some critical processes to be killed during setup. - Temporarily revert this change until we find a better solution. try { mActivityManager.clearApplicationUserData(packageName, observer); } catch (RemoteException e) { // can't happen because the activity manager is in this process } - */ - mPackageManager.clearApplicationUserData(packageName, observer); // only wait 10 seconds for the clear data to happen long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; @@ -984,16 +1179,58 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Get the restore-set token for the best-available restore set for this package: + // the active set if possible, else the ancestral one. Returns zero if none available. + long getAvailableRestoreToken(String packageName) { + long token = mAncestralToken; + synchronized (mQueueLock) { + if (mEverStoredApps.contains(packageName)) { + token = mCurrentToken; + } + } + return token; + } + + // ----- + // Utility methods used by the asynchronous-with-timeout backup/restore operations + boolean waitUntilOperationComplete(int token) { + int finalState = OP_PENDING; + synchronized (mCurrentOpLock) { + try { + while ((finalState = mCurrentOperations.get(token, OP_TIMEOUT)) == OP_PENDING) { + try { + mCurrentOpLock.wait(); + } catch (InterruptedException e) {} + } + } catch (IndexOutOfBoundsException e) { + // the operation has been mysteriously cleared from our + // bookkeeping -- consider this a success and ignore it. + } + } + mBackupHandler.removeMessages(MSG_TIMEOUT); + if (DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token) + + " complete: finalState=" + finalState); + return finalState == OP_ACKNOWLEDGED; + } + + void prepareOperationTimeout(int token, long interval) { + if (DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token) + + " interval=" + interval); + mCurrentOperations.put(token, OP_PENDING); + Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0); + mBackupHandler.sendMessageDelayed(msg, interval); + } + // ----- Back up a set of applications via a worker thread ----- - class PerformBackupThread extends Thread { + class PerformBackupTask implements Runnable { private static final String TAG = "PerformBackupThread"; IBackupTransport mTransport; ArrayList<BackupRequest> mQueue; File mStateDir; File mJournal; - public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue, + public PerformBackupTask(IBackupTransport transport, ArrayList<BackupRequest> queue, File journal) { mTransport = transport; mQueue = queue; @@ -1006,29 +1243,28 @@ class BackupManagerService extends IBackupManager.Stub { } } - @Override public void run() { int status = BackupConstants.TRANSPORT_OK; long startRealtime = SystemClock.elapsedRealtime(); - if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); + if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); // Backups run at background priority Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); try { - EventLog.writeEvent(BACKUP_START_EVENT, mTransport.transportDirName()); + EventLog.writeEvent(EventLogTags.BACKUP_START, mTransport.transportDirName()); // If we haven't stored package manager metadata yet, we must init the transport. File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL); if (status == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) { - Log.i(TAG, "Initializing (wiping) backup state and transport storage"); + Slog.i(TAG, "Initializing (wiping) backup state and transport storage"); resetBackupState(mStateDir); // Just to make sure. status = mTransport.initializeDevice(); if (status == BackupConstants.TRANSPORT_OK) { - EventLog.writeEvent(BACKUP_INITIALIZE_EVENT); + EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); } else { - EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, "(initialize)"); - Log.e(TAG, "Transport error in initializeDevice()"); + EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); + Slog.e(TAG, "Transport error in initializeDevice()"); } } @@ -1056,30 +1292,40 @@ class BackupManagerService extends IBackupManager.Stub { status = mTransport.finishBackup(); if (status == BackupConstants.TRANSPORT_OK) { int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); - EventLog.writeEvent(BACKUP_SUCCESS_EVENT, mQueue.size(), millis); + EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, mQueue.size(), millis); } else { - EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, "(finish)"); - Log.e(TAG, "Transport error in finishBackup()"); + EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(finish)"); + Slog.e(TAG, "Transport error in finishBackup()"); } } if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) { // The backend reports that our dataset has been wiped. We need to // reset all of our bookkeeping and instead run a new backup pass for - // everything. This must come after mBackupOrRestoreInProgress is cleared. - EventLog.writeEvent(BACKUP_RESET_EVENT, mTransport.transportDirName()); + // everything. + EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName()); resetBackupState(mStateDir); } } catch (Exception e) { - Log.e(TAG, "Error in backup thread", e); + Slog.e(TAG, "Error in backup thread", e); status = BackupConstants.TRANSPORT_ERROR; } finally { + // If everything actually went through and this is the first time we've + // done a backup, we can now record what the current backup dataset token + // is. + if ((mCurrentToken == 0) && (status != BackupConstants.TRANSPORT_OK)) { + try { + mCurrentToken = mTransport.getCurrentRestoreSet(); + } catch (RemoteException e) { /* cannot happen */ } + writeRestoreTokens(); + } + // If things went wrong, we need to re-stage the apps we had expected // to be backing up in this pass. This journals the package names in // the current active pending-backup file, not in the we are holding // here in mJournal. if (status != BackupConstants.TRANSPORT_OK) { - Log.w(TAG, "Backup pass unsuccessful, restaging"); + Slog.w(TAG, "Backup pass unsuccessful, restaging"); for (BackupRequest req : mQueue) { dataChanged(req.appInfo.packageName); } @@ -1096,17 +1342,11 @@ class BackupManagerService extends IBackupManager.Stub { // re-enqueued all of these packages in the current active journal. // Either way, we no longer need this pass's journal. if (mJournal != null && !mJournal.delete()) { - Log.e(TAG, "Unable to remove backup journal file " + mJournal); - } - - // Only once we're entirely finished do we indicate our completion - // and release the wakelock - synchronized (mQueueLock) { - mBackupOrRestoreInProgress = false; + Slog.e(TAG, "Unable to remove backup journal file " + mJournal); } + // Only once we're entirely finished do we release the wakelock if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) { - // This must come after mBackupOrRestoreInProgress is cleared. backupNow(); } @@ -1116,16 +1356,7 @@ class BackupManagerService extends IBackupManager.Stub { private int doQueuedBackups(IBackupTransport transport) { for (BackupRequest request : mQueue) { - Log.d(TAG, "starting agent for backup of " + request); - - // Don't run backup, even if requested, if the target app does not have - // the requisite permission - if (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA, - request.appInfo.packageName) != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Skipping backup of unprivileged package " - + request.appInfo.packageName); - continue; - } + Slog.d(TAG, "starting agent for backup of " + request); IBackupAgent agent = null; int mode = (request.fullBackup) @@ -1139,7 +1370,7 @@ class BackupManagerService extends IBackupManager.Stub { } } catch (SecurityException ex) { // Try for the next one. - Log.d(TAG, "error in bind/backup", ex); + Slog.d(TAG, "error in bind/backup", ex); } finally { try { // unbind even on timeout, just in case mActivityManager.unbindBackupAgent(request.appInfo); @@ -1153,7 +1384,7 @@ class BackupManagerService extends IBackupManager.Stub { private int processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) { final String packageName = request.appInfo.packageName; - if (DEBUG) Log.d(TAG, "processOneBackup doBackup() on " + packageName); + if (DEBUG) Slog.d(TAG, "processOneBackup doBackup() on " + packageName); File savedStateName = new File(mStateDir, packageName); File backupDataName = new File(mDataDir, packageName + ".data"); @@ -1164,6 +1395,7 @@ class BackupManagerService extends IBackupManager.Stub { ParcelFileDescriptor newState = null; PackageInfo packInfo; + int token = mTokenGenerator.nextInt(); try { // Look up the package info & signatures. This is first so that if it // throws an exception, there's no file setup yet that would need to @@ -1195,13 +1427,21 @@ class BackupManagerService extends IBackupManager.Stub { ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE); - // Run the target's backup pass - agent.doBackup(savedState, backupData, newState); + // Initiate the target's backup pass + prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL); + agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder); + boolean success = waitUntilOperationComplete(token); + + if (!success) { + // timeout -- bail out into the failed-transaction logic + throw new RuntimeException("Backup timeout"); + } + logBackupComplete(packageName); - if (DEBUG) Log.v(TAG, "doBackup() success"); + if (DEBUG) Slog.v(TAG, "doBackup() success"); } catch (Exception e) { - Log.e(TAG, "Error backing up " + packageName, e); - EventLog.writeEvent(BACKUP_AGENT_FAILURE_EVENT, packageName, e.toString()); + Slog.e(TAG, "Error backing up " + packageName, e); + EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString()); backupDataName.delete(); newStateName.delete(); return BackupConstants.TRANSPORT_ERROR; @@ -1210,6 +1450,9 @@ class BackupManagerService extends IBackupManager.Stub { try { if (backupData != null) backupData.close(); } catch (IOException e) {} try { if (newState != null) newState.close(); } catch (IOException e) {} savedState = backupData = newState = null; + synchronized (mCurrentOpLock) { + mCurrentOperations.clear(); + } } // Now propagate the newly-backed-up data to the transport @@ -1232,7 +1475,7 @@ class BackupManagerService extends IBackupManager.Stub { result = transport.finishBackup(); } } else { - if (DEBUG) Log.i(TAG, "no backup data written; not calling transport"); + if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport"); } // After successful transport, delete the now-stale data @@ -1241,13 +1484,13 @@ class BackupManagerService extends IBackupManager.Stub { if (result == BackupConstants.TRANSPORT_OK) { backupDataName.delete(); newStateName.renameTo(savedStateName); - EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size); + EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, packageName, size); } else { - EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName); + EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName); } } catch (Exception e) { - Log.e(TAG, "Transport error backing up " + packageName, e); - EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName); + Slog.e(TAG, "Transport error backing up " + packageName, e); + EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName); result = BackupConstants.TRANSPORT_ERROR; } finally { try { if (backupData != null) backupData.close(); } catch (IOException e) {} @@ -1267,14 +1510,14 @@ class BackupManagerService extends IBackupManager.Stub { // partition will be signed with the device's platform certificate, so on // different phones the same system app will have different signatures.) if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - if (DEBUG) Log.v(TAG, "System app " + target.packageName + " - skipping sig check"); + if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); return true; } // Allow unsigned apps, but not signed on one device and unsigned on the other // !!! TODO: is this the right policy? Signature[] deviceSigs = target.signatures; - if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs + if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceSigs); if ((storedSigs == null || storedSigs.length == 0) && (deviceSigs == null || deviceSigs.length == 0)) { @@ -1305,11 +1548,13 @@ class BackupManagerService extends IBackupManager.Stub { return true; } - class PerformRestoreThread extends Thread { + class PerformRestoreTask implements Runnable { private IBackupTransport mTransport; private IRestoreObserver mObserver; private long mToken; + private PackageInfo mTargetPackage; private File mStateDir; + private int mPmToken; class RestoreRequest { public PackageInfo app; @@ -1321,12 +1566,13 @@ class BackupManagerService extends IBackupManager.Stub { } } - PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer, - long restoreSetToken) { + PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer, + long restoreSetToken, PackageInfo targetPackage, int pmToken) { mTransport = transport; - Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver); mObserver = observer; mToken = restoreSetToken; + mTargetPackage = targetPackage; + mPmToken = pmToken; try { mStateDir = new File(mBaseStateDir, transport.transportDirName()); @@ -1335,36 +1581,19 @@ class BackupManagerService extends IBackupManager.Stub { } } - @Override public void run() { long startRealtime = SystemClock.elapsedRealtime(); - if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport - + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)); - /** - * Restore sequence: - * - * 1. get the restore set description for our identity - * 2. for each app in the restore set: - * 2.a. if it's restorable on this device, add it to the restore queue - * 3. for each app in the restore queue: - * 3.a. clear the app data - * 3.b. get the restore data for the app from the transport - * 3.c. launch the backup agent for the app - * 3.d. agent.doRestore() with the data from the server - * 3.e. unbind the agent [and kill the app?] - * 4. shut down the transport - * - * On errors, we try our best to recover and move on to the next - * application, but if necessary we abort the whole operation -- - * the user is waiting, after al. - */ + if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport + + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken) + + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken); + PackageManagerBackupAgent pmAgent = null; int error = -1; // assume error // build the set of apps to restore try { // TODO: Log this before getAvailableRestoreSets, somehow - EventLog.writeEvent(RESTORE_START_EVENT, mTransport.transportDirName(), mToken); + EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken); // Get the list of all packages which have backup enabled. // (Include the Package Manager metadata pseudo-package first.) @@ -1374,7 +1603,12 @@ class BackupManagerService extends IBackupManager.Stub { restorePackages.add(omPackage); List<PackageInfo> agentPackages = allAgentPackages(); - restorePackages.addAll(agentPackages); + if (mTargetPackage == null) { + restorePackages.addAll(agentPackages); + } else { + // Just one package to attempt restore of + restorePackages.add(mTargetPackage); + } // let the observer know that we're running if (mObserver != null) { @@ -1383,38 +1617,38 @@ class BackupManagerService extends IBackupManager.Stub { // its startRestore() runs? mObserver.restoreStarting(restorePackages.size()); } catch (RemoteException e) { - Log.d(TAG, "Restore observer died at restoreStarting"); + Slog.d(TAG, "Restore observer died at restoreStarting"); mObserver = null; } } if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) != BackupConstants.TRANSPORT_OK) { - Log.e(TAG, "Error starting restore operation"); - EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT); + Slog.e(TAG, "Error starting restore operation"); + EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); return; } String packageName = mTransport.nextRestorePackage(); if (packageName == null) { - Log.e(TAG, "Error getting first restore package"); - EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT); + Slog.e(TAG, "Error getting first restore package"); + EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); return; } else if (packageName.equals("")) { - Log.i(TAG, "No restore data available"); + Slog.i(TAG, "No restore data available"); int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); - EventLog.writeEvent(RESTORE_SUCCESS_EVENT, 0, millis); + EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis); return; } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) { - Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL + Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL + "\", found only \"" + packageName + "\""); - EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, PACKAGE_MANAGER_SENTINEL, + EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL, "Package manager data missing"); return; } // Pull the Package Manager metadata from the restore set first - PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + pmAgent = new PackageManagerBackupAgent( mPackageManager, agentPackages); processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind())); @@ -1422,8 +1656,8 @@ class BackupManagerService extends IBackupManager.Stub { // signature/version verification etc, so we simply do not proceed with // the restore operation. if (!pmAgent.hasMetadata()) { - Log.e(TAG, "No restore metadata available, so not restoring settings"); - EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, PACKAGE_MANAGER_SENTINEL, + Slog.e(TAG, "No restore metadata available, so not restoring settings"); + EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL, "Package manager restore metadata missing"); return; } @@ -1433,26 +1667,27 @@ class BackupManagerService extends IBackupManager.Stub { packageName = mTransport.nextRestorePackage(); if (packageName == null) { - Log.e(TAG, "Error getting next restore package"); - EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT); + Slog.e(TAG, "Error getting next restore package"); + EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); return; } else if (packageName.equals("")) { + if (DEBUG) Slog.v(TAG, "No next package, finishing restore"); break; } if (mObserver != null) { try { - mObserver.onUpdate(count); + mObserver.onUpdate(count, packageName); } catch (RemoteException e) { - Log.d(TAG, "Restore observer died in onUpdate"); + Slog.d(TAG, "Restore observer died in onUpdate"); mObserver = null; } } Metadata metaInfo = pmAgent.getRestoredMetadata(packageName); if (metaInfo == null) { - Log.e(TAG, "Missing metadata for " + packageName); - EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, + Slog.e(TAG, "Missing metadata for " + packageName); + EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, "Package metadata missing"); continue; } @@ -1462,50 +1697,50 @@ class BackupManagerService extends IBackupManager.Stub { int flags = PackageManager.GET_SIGNATURES; packageInfo = mPackageManager.getPackageInfo(packageName, flags); } catch (NameNotFoundException e) { - Log.e(TAG, "Invalid package restoring data", e); - EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, + Slog.e(TAG, "Invalid package restoring data", e); + EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, "Package missing on device"); continue; } if (metaInfo.versionCode > packageInfo.versionCode) { - String message = "Version " + metaInfo.versionCode - + " > installed version " + packageInfo.versionCode; - Log.w(TAG, "Package " + packageName + ": " + message); - EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, message); - continue; + // Data is from a "newer" version of the app than we have currently + // installed. If the app has not declared that it is prepared to + // handle this case, we do not attempt the restore. + if ((packageInfo.applicationInfo.flags + & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { + String message = "Version " + metaInfo.versionCode + + " > installed version " + packageInfo.versionCode; + Slog.w(TAG, "Package " + packageName + ": " + message); + EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, + packageName, message); + continue; + } else { + if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode + + " > installed " + packageInfo.versionCode + + " but restoreAnyVersion"); + } } if (!signaturesMatch(metaInfo.signatures, packageInfo)) { - Log.w(TAG, "Signature mismatch restoring " + packageName); - EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, + Slog.w(TAG, "Signature mismatch restoring " + packageName); + EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, "Signature mismatch"); continue; } - if (DEBUG) Log.v(TAG, "Package " + packageName + if (DEBUG) Slog.v(TAG, "Package " + packageName + " restore version [" + metaInfo.versionCode + "] is compatible with installed version [" + packageInfo.versionCode + "]"); - // Now perform the actual restore: first clear the app's data - // if appropriate - clearApplicationDataSynchronous(packageName); - - // Then set up and bind the agent (with a restricted Application object - // unless the application says otherwise) - boolean useRealApp = (packageInfo.applicationInfo.flags - & ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION) != 0; - if (DEBUG && useRealApp) { - Log.v(TAG, "agent requires real Application subclass for restore"); - } + // Then set up and bind the agent IBackupAgent agent = bindToAgentSynchronous( packageInfo.applicationInfo, - (useRealApp ? IApplicationThread.BACKUP_MODE_INCREMENTAL - : IApplicationThread.BACKUP_MODE_RESTORE)); + IApplicationThread.BACKUP_MODE_INCREMENTAL); if (agent == null) { - Log.w(TAG, "Can't find backup agent for " + packageName); - EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, + Slog.w(TAG, "Can't find backup agent for " + packageName); + EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, "Restore agent missing"); continue; } @@ -1521,10 +1756,12 @@ class BackupManagerService extends IBackupManager.Stub { // The agent was probably running with a stub Application object, // which isn't a valid run mode for the main app logic. Shut // down the app so that next time it's launched, it gets the - // usual full initialization. - if ((packageInfo.applicationInfo.flags + // usual full initialization. Note that this is only done for + // full-system restores: when a single app has requested a restore, + // it is explicitly not killed following that operation. + if (mTargetPackage == null && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) { - if (DEBUG) Log.d(TAG, "Restore complete, killing host process of " + if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of " + packageInfo.applicationInfo.processName); mActivityManager.killApplicationProcess( packageInfo.applicationInfo.processName, @@ -1536,30 +1773,45 @@ class BackupManagerService extends IBackupManager.Stub { // if we get this far, report success to the observer error = 0; int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); - EventLog.writeEvent(RESTORE_SUCCESS_EVENT, count, millis); + EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, count, millis); } catch (Exception e) { - Log.e(TAG, "Error in restore thread", e); + Slog.e(TAG, "Error in restore thread", e); } finally { - if (DEBUG) Log.d(TAG, "finishing restore mObserver=" + mObserver); + if (DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver); try { mTransport.finishRestore(); } catch (RemoteException e) { - Log.e(TAG, "Error finishing restore", e); + Slog.e(TAG, "Error finishing restore", e); } if (mObserver != null) { try { mObserver.restoreFinished(error); } catch (RemoteException e) { - Log.d(TAG, "Restore observer died at restoreFinished"); + Slog.d(TAG, "Restore observer died at restoreFinished"); } } - // done; we can finally release the wakelock - synchronized (mQueueLock) { - mBackupOrRestoreInProgress = false; + // If this was a restoreAll operation, record that this was our + // ancestral dataset, as well as the set of apps that are possibly + // restoreable from the dataset + if (mTargetPackage == null && pmAgent != null) { + mAncestralPackages = pmAgent.getRestoredPackages(); + mAncestralToken = mToken; + writeRestoreTokens(); + } + + // We must under all circumstances tell the Package Manager to + // proceed with install notifications if it's waiting for us. + if (mPmToken > 0) { + if (DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken); + try { + mPackageManagerBinder.finishPackageInstall(mPmToken); + } catch (RemoteException e) { /* can't happen */ } } + + // done; we can finally release the wakelock mWakelock.release(); } } @@ -1569,13 +1821,7 @@ class BackupManagerService extends IBackupManager.Stub { // !!! TODO: actually run the restore through mTransport final String packageName = app.packageName; - if (DEBUG) Log.d(TAG, "processOneRestore packageName=" + packageName); - - // Don't restore to unprivileged packages - if (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA, - packageName) != PackageManager.PERMISSION_GRANTED) { - Log.d(TAG, "Skipping restore of unprivileged package " + packageName); - } + if (DEBUG) Slog.d(TAG, "processOneRestore packageName=" + packageName); // !!! TODO: get the dirs from the transport File backupDataName = new File(mDataDir, packageName + ".restore"); @@ -1585,6 +1831,7 @@ class BackupManagerService extends IBackupManager.Stub { ParcelFileDescriptor backupData = null; ParcelFileDescriptor newState = null; + int token = mTokenGenerator.nextInt(); try { // Run the transport's restore pass backupData = ParcelFileDescriptor.open(backupDataName, @@ -1593,8 +1840,8 @@ class BackupManagerService extends IBackupManager.Stub { ParcelFileDescriptor.MODE_TRUNCATE); if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) { - Log.e(TAG, "Error getting restore data for " + packageName); - EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT); + Slog.e(TAG, "Error getting restore data for " + packageName); + EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); return; } @@ -1608,7 +1855,14 @@ class BackupManagerService extends IBackupManager.Stub { ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE); - agent.doRestore(backupData, appVersionCode, newState); + // Kick off the restore, checking for hung agents + prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL); + agent.doRestore(backupData, appVersionCode, newState, token, mBackupManagerBinder); + boolean success = waitUntilOperationComplete(token); + + if (!success) { + throw new RuntimeException("restore timeout"); + } // if everything went okay, remember the recorded state now // @@ -1627,10 +1881,10 @@ class BackupManagerService extends IBackupManager.Stub { //newStateName.renameTo(savedStateName); // TODO: replace with this int size = (int) backupDataName.length(); - EventLog.writeEvent(RESTORE_PACKAGE_EVENT, packageName, size); + EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, packageName, size); } catch (Exception e) { - Log.e(TAG, "Error restoring data for " + packageName, e); - EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, e.toString()); + Slog.e(TAG, "Error restoring data for " + packageName, e); + EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString()); // If the agent fails restore, it might have put the app's data // into an incoherent state. For consistency we wipe its data @@ -1641,20 +1895,20 @@ class BackupManagerService extends IBackupManager.Stub { try { if (backupData != null) backupData.close(); } catch (IOException e) {} try { if (newState != null) newState.close(); } catch (IOException e) {} backupData = newState = null; + mCurrentOperations.delete(token); } } } - class PerformClearThread extends Thread { + class PerformClearTask implements Runnable { IBackupTransport mTransport; PackageInfo mPackage; - PerformClearThread(IBackupTransport transport, PackageInfo packageInfo) { + PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) { mTransport = transport; mPackage = packageInfo; } - @Override public void run() { try { // Clear the on-device backup state to ensure a full backup next time @@ -1676,33 +1930,29 @@ class BackupManagerService extends IBackupManager.Stub { } // Last but not least, release the cpu - synchronized (mQueueLock) { - mBackupOrRestoreInProgress = false; - } mWakelock.release(); } } } - class PerformInitializeThread extends Thread { + class PerformInitializeTask implements Runnable { HashSet<String> mQueue; - PerformInitializeThread(HashSet<String> transportNames) { + PerformInitializeTask(HashSet<String> transportNames) { mQueue = transportNames; } - @Override public void run() { try { for (String transportName : mQueue) { IBackupTransport transport = getTransport(transportName); if (transport == null) { - Log.e(TAG, "Requested init for " + transportName + " but not found"); + Slog.e(TAG, "Requested init for " + transportName + " but not found"); continue; } - Log.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); - EventLog.writeEvent(BACKUP_START_EVENT, transport.transportDirName()); + Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); + EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName()); long startRealtime = SystemClock.elapsedRealtime(); int status = transport.initializeDevice(); @@ -1712,25 +1962,25 @@ class BackupManagerService extends IBackupManager.Stub { // Okay, the wipe really happened. Clean up our local bookkeeping. if (status == BackupConstants.TRANSPORT_OK) { - Log.i(TAG, "Device init successful"); + Slog.i(TAG, "Device init successful"); int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); - EventLog.writeEvent(BACKUP_INITIALIZE_EVENT); + EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); resetBackupState(new File(mBaseStateDir, transport.transportDirName())); - EventLog.writeEvent(BACKUP_SUCCESS_EVENT, 0, millis); + EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); synchronized (mQueueLock) { recordInitPendingLocked(false, transportName); } } else { // If this didn't work, requeue this one and try again // after a suitable interval - Log.e(TAG, "Transport error in initializeDevice()"); - EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, "(initialize)"); + Slog.e(TAG, "Transport error in initializeDevice()"); + EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); synchronized (mQueueLock) { recordInitPendingLocked(true, transportName); } // do this via another alarm to make sure of the wakelock states long delay = transport.requestBackupTime(); - if (DEBUG) Log.w(TAG, "init failed on " + if (DEBUG) Slog.w(TAG, "init failed on " + transportName + " resched in " + delay); mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delay, mRunInitIntent); @@ -1739,12 +1989,9 @@ class BackupManagerService extends IBackupManager.Stub { } catch (RemoteException e) { // can't happen; the transports are local } catch (Exception e) { - Log.e(TAG, "Unexpected error performing init", e); + Slog.e(TAG, "Unexpected error performing init", e); } finally { - // Done; indicate that we're finished and release the wakelock - synchronized (mQueueLock) { - mInitInProgress = false; - } + // Done; release the wakelock mWakelock.release(); } } @@ -1757,7 +2004,7 @@ class BackupManagerService extends IBackupManager.Stub { // Record that we need a backup pass for the caller. Since multiple callers // may share a uid, we need to note all candidates within that uid and schedule // a backup pass for each of them. - EventLog.writeEvent(BACKUP_DATA_CHANGED_EVENT, packageName); + EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName); // If the caller does not hold the BACKUP permission, it can only request a // backup of its own data. @@ -1795,9 +2042,9 @@ class BackupManagerService extends IBackupManager.Stub { if (DEBUG) { int numKeys = mPendingBackups.size(); - Log.d(TAG, "Now awaiting backup for " + numKeys + " participants:"); + Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:"); for (BackupRequest b : mPendingBackups.values()) { - Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName); + Slog.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName); } } } @@ -1805,7 +2052,7 @@ class BackupManagerService extends IBackupManager.Stub { } } } else { - Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" + Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" + " uid=" + Binder.getCallingUid()); } } @@ -1818,7 +2065,7 @@ class BackupManagerService extends IBackupManager.Stub { out.seek(out.length()); out.writeUTF(str); } catch (IOException e) { - Log.e(TAG, "Can't write " + str + " to backup journal", e); + Slog.e(TAG, "Can't write " + str + " to backup journal", e); mJournal = null; } finally { try { if (out != null) out.close(); } catch (IOException e) {} @@ -1827,12 +2074,12 @@ class BackupManagerService extends IBackupManager.Stub { // Clear the given package's backup data from the current transport public void clearBackupData(String packageName) { - if (DEBUG) Log.v(TAG, "clearBackupData() of " + packageName); + if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName); PackageInfo info; try { info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); } catch (NameNotFoundException e) { - Log.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); + Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); return; } @@ -1845,7 +2092,7 @@ class BackupManagerService extends IBackupManager.Stub { } else { // a caller with full permission can ask to back up any participating app // !!! TODO: allow data-clear of ANY app? - if (DEBUG) Log.v(TAG, "Privileged caller, allowing clear of other apps"); + if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps"); apps = new HashSet<ApplicationInfo>(); int N = mBackupParticipants.size(); for (int i = 0; i < N; i++) { @@ -1859,7 +2106,7 @@ class BackupManagerService extends IBackupManager.Stub { // now find the given package in the set of candidate apps for (ApplicationInfo app : apps) { if (app.packageName.equals(packageName)) { - if (DEBUG) Log.v(TAG, "Found the app - running clear process"); + if (DEBUG) Slog.v(TAG, "Found the app - running clear process"); // found it; fire off the clear request synchronized (mQueueLock) { long oldId = Binder.clearCallingIdentity(); @@ -1879,7 +2126,7 @@ class BackupManagerService extends IBackupManager.Stub { public void backupNow() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow"); - if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass"); + if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); synchronized (mQueueLock) { // Because the alarms we are using can jitter, and we want an *immediate* // backup pass to happen, we restart the timer beginning with "next time," @@ -1889,7 +2136,7 @@ class BackupManagerService extends IBackupManager.Stub { mRunBackupIntent.send(); } catch (PendingIntent.CanceledException e) { // should never happen - Log.e(TAG, "run-backup intent cancelled!"); + Slog.e(TAG, "run-backup intent cancelled!"); } } } @@ -1899,7 +2146,7 @@ class BackupManagerService extends IBackupManager.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "setBackupEnabled"); - Log.i(TAG, "Backup enabled => " + enable); + Slog.i(TAG, "Backup enabled => " + enable); boolean wasEnabled = mEnabled; synchronized (this) { @@ -1914,7 +2161,7 @@ class BackupManagerService extends IBackupManager.Stub { startBackupAlarmsLocked(BACKUP_INTERVAL); } else if (!enable) { // No longer enabled, so stop running backups - if (DEBUG) Log.i(TAG, "Opting out of backup"); + if (DEBUG) Slog.i(TAG, "Opting out of backup"); mAlarmManager.cancel(mRunBackupIntent); @@ -1939,6 +2186,20 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Enable/disable automatic restore of app data at install time + public void setAutoRestore(boolean doAutoRestore) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, + "setBackupEnabled"); + + Slog.i(TAG, "Auto restore => " + doAutoRestore); + + synchronized (this) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0); + mAutoRestore = doAutoRestore; + } + } + // Mark the backup service as having been provisioned public void setBackupProvisioned(boolean available) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, @@ -1957,7 +2218,7 @@ class BackupManagerService extends IBackupManager.Stub { startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL); } else if (!available) { // No longer enabled, so stop running backups - Log.w(TAG, "Backup service no longer provisioned"); + Slog.w(TAG, "Backup service no longer provisioned"); mAlarmManager.cancel(mRunBackupIntent); } } @@ -1986,7 +2247,7 @@ class BackupManagerService extends IBackupManager.Stub { public String getCurrentTransport() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "getCurrentTransport"); - Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); + if (DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); return mCurrentTransport; } @@ -2022,10 +2283,10 @@ class BackupManagerService extends IBackupManager.Stub { mCurrentTransport = transport; Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT, transport); - Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport + Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport + " returning " + prevTransport); } else { - Log.w(TAG, "Attempt to select unavailable transport " + transport); + Slog.w(TAG, "Attempt to select unavailable transport " + transport); } return prevTransport; } @@ -2036,12 +2297,12 @@ class BackupManagerService extends IBackupManager.Stub { public void agentConnected(String packageName, IBinder agentBinder) { synchronized(mAgentConnectLock) { if (Binder.getCallingUid() == Process.SYSTEM_UID) { - Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); + Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder); mConnectedAgent = agent; mConnecting = false; } else { - Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid() + Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() + " claiming agent connected"); } mAgentConnectLock.notifyAll(); @@ -2058,83 +2319,132 @@ class BackupManagerService extends IBackupManager.Stub { mConnectedAgent = null; mConnecting = false; } else { - Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid() + Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() + " claiming agent disconnected"); } mAgentConnectLock.notifyAll(); } } + // An application being installed will need a restore pass, then the Package Manager + // will need to be told when the restore is finished. + public void restoreAtInstall(String packageName, int token) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() + + " attemping install-time restore"); + return; + } + + long restoreSet = getAvailableRestoreToken(packageName); + if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName + + " token=" + Integer.toHexString(token)); + + if (mAutoRestore && mProvisioned && restoreSet != 0) { + // okay, we're going to attempt a restore of this package from this restore set. + // The eventual message back into the Package Manager to run the post-install + // steps for 'token' will be issued from the restore handling code. + + // We can use a synthetic PackageInfo here because: + // 1. We know it's valid, since the Package Manager supplied the name + // 2. Only the packageName field will be used by the restore code + PackageInfo pkg = new PackageInfo(); + pkg.packageName = packageName; + + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); + msg.obj = new RestoreParams(getTransport(mCurrentTransport), null, + restoreSet, pkg, token); + mBackupHandler.sendMessage(msg); + } else { + // Auto-restore disabled or no way to attempt a restore; just tell the Package + // Manager to proceed with the post-install handling for this package. + if (DEBUG) Slog.v(TAG, "No restore set -- skipping restore"); + try { + mPackageManagerBinder.finishPackageInstall(token); + } catch (RemoteException e) { /* can't happen */ } + } + } + // Hand off a restore session public IRestoreSession beginRestoreSession(String transport) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "beginRestoreSession"); synchronized(this) { if (mActiveRestoreSession != null) { - Log.d(TAG, "Restore session requested but one already active"); + Slog.d(TAG, "Restore session requested but one already active"); return null; } - mActiveRestoreSession = new RestoreSession(transport); + mActiveRestoreSession = new ActiveRestoreSession(transport); } return mActiveRestoreSession; } + // Note that a currently-active backup agent has notified us that it has + // completed the given outstanding asynchronous backup/restore operation. + public void opComplete(int token) { + synchronized (mCurrentOpLock) { + if (DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token)); + mCurrentOperations.put(token, OP_ACKNOWLEDGED); + mCurrentOpLock.notifyAll(); + } + } + // ----- Restore session ----- - class RestoreSession extends IRestoreSession.Stub { + class ActiveRestoreSession extends IRestoreSession.Stub { private static final String TAG = "RestoreSession"; private IBackupTransport mRestoreTransport = null; RestoreSet[] mRestoreSets = null; - RestoreSession(String transport) { + ActiveRestoreSession(String transport) { mRestoreTransport = getTransport(transport); } // --- Binder interface --- - public synchronized RestoreSet[] getAvailableRestoreSets() { + public synchronized int getAvailableRestoreSets(IRestoreObserver observer) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "getAvailableRestoreSets"); + if (observer == null) { + throw new IllegalArgumentException("Observer must not be null"); + } + long oldId = Binder.clearCallingIdentity(); try { if (mRestoreTransport == null) { - Log.w(TAG, "Null transport getting restore sets"); - return null; - } - if (mRestoreSets == null) { // valid transport; do the one-time fetch - mRestoreSets = mRestoreTransport.getAvailableRestoreSets(); - if (mRestoreSets == null) EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT); + Slog.w(TAG, "Null transport getting restore sets"); + return -1; } - return mRestoreSets; + // spin off the transport request to our service thread + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS, + new RestoreGetSetsParams(mRestoreTransport, this, observer)); + mBackupHandler.sendMessage(msg); + return 0; } catch (Exception e) { - Log.e(TAG, "Error in getAvailableRestoreSets", e); - return null; + Slog.e(TAG, "Error in getAvailableRestoreSets", e); + return -1; + } finally { + Binder.restoreCallingIdentity(oldId); } } - public synchronized int performRestore(long token, IRestoreObserver observer) { + public synchronized int restoreAll(long token, IRestoreObserver observer) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "performRestore"); - if (DEBUG) Log.d(TAG, "performRestore token=" + Long.toHexString(token) + if (DEBUG) Slog.d(TAG, "performRestore token=" + Long.toHexString(token) + " observer=" + observer); if (mRestoreTransport == null || mRestoreSets == null) { - Log.e(TAG, "Ignoring performRestore() with no restore set"); + Slog.e(TAG, "Ignoring performRestore() with no restore set"); return -1; } synchronized (mQueueLock) { - if (mBackupOrRestoreInProgress) { - Log.e(TAG, "Backup pass in progress, restore aborted"); - return -1; - } - for (int i = 0; i < mRestoreSets.length; i++) { if (token == mRestoreSets[i].token) { long oldId = Binder.clearCallingIdentity(); - // Suppress backups until the restore operation is finished - mBackupOrRestoreInProgress = true; mWakelock.acquire(); Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); msg.obj = new RestoreParams(mRestoreTransport, observer, token); @@ -2145,23 +2455,75 @@ class BackupManagerService extends IBackupManager.Stub { } } - Log.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); + Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); return -1; } + public synchronized int restorePackage(String packageName, IRestoreObserver observer) { + if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); + + PackageInfo app = null; + try { + app = mPackageManager.getPackageInfo(packageName, 0); + } catch (NameNotFoundException nnf) { + Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); + return -1; + } + + // If the caller is not privileged and is not coming from the target + // app's uid, throw a permission exception back to the caller. + int perm = mContext.checkPermission(android.Manifest.permission.BACKUP, + Binder.getCallingPid(), Binder.getCallingUid()); + if ((perm == PackageManager.PERMISSION_DENIED) && + (app.applicationInfo.uid != Binder.getCallingUid())) { + Slog.w(TAG, "restorePackage: bad packageName=" + packageName + + " or calling uid=" + Binder.getCallingUid()); + throw new SecurityException("No permission to restore other packages"); + } + + // If the package has no backup agent, we obviously cannot proceed + if (app.applicationInfo.backupAgentName == null) { + Slog.w(TAG, "Asked to restore package " + packageName + " with no agent"); + return -1; + } + + // So far so good; we're allowed to try to restore this package. Now + // check whether there is data for it in the current dataset, falling back + // to the ancestral dataset if not. + long token = getAvailableRestoreToken(packageName); + + // If we didn't come up with a place to look -- no ancestral dataset and + // the app has never been backed up from this device -- there's nothing + // to do but return failure. + if (token == 0) { + return -1; + } + + // Ready to go: enqueue the restore request and claim success + long oldId = Binder.clearCallingIdentity(); + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); + msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0); + mBackupHandler.sendMessage(msg); + Binder.restoreCallingIdentity(oldId); + return 0; + } + public synchronized void endRestoreSession() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "endRestoreSession"); - if (DEBUG) Log.d(TAG, "endRestoreSession"); + if (DEBUG) Slog.d(TAG, "endRestoreSession"); synchronized (this) { + long oldId = Binder.clearCallingIdentity(); try { if (mRestoreTransport != null) mRestoreTransport.finishRestore(); } catch (Exception e) { - Log.e(TAG, "Error in finishRestore", e); + Slog.e(TAG, "Error in finishRestore", e); } finally { mRestoreTransport = null; + Binder.restoreCallingIdentity(oldId); } } @@ -2169,7 +2531,7 @@ class BackupManagerService extends IBackupManager.Stub { if (BackupManagerService.this.mActiveRestoreSession == this) { BackupManagerService.this.mActiveRestoreSession = null; } else { - Log.e(TAG, "ending non-current restore session"); + Slog.e(TAG, "ending non-current restore session"); } } } @@ -2181,9 +2543,8 @@ class BackupManagerService extends IBackupManager.Stub { synchronized (mQueueLock) { pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") + " / " + (!mProvisioned ? "not " : "") + "provisioned / " - + (!mBackupOrRestoreInProgress ? "not " : "") + "in progress / " - + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init / " - + (!mInitInProgress ? "not " : "") + "initializing"); + + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init"); + pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); pw.println("Last backup pass: " + mLastBackupPass + " (now = " + System.currentTimeMillis() + ')'); pw.println(" next scheduled: " + mNextBackupPass); @@ -2197,7 +2558,7 @@ class BackupManagerService extends IBackupManager.Stub { pw.println(" " + f.getName() + " - " + f.length() + " state bytes"); } } catch (RemoteException e) { - Log.e(TAG, "Error in transportDirName()", e); + Slog.e(TAG, "Error in transportDirName()", e); pw.println(" Error: " + e); } } @@ -2219,6 +2580,14 @@ class BackupManagerService extends IBackupManager.Stub { } } + pw.println("Ancestral packages: " + + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); + if (mAncestralPackages != null) { + for (String pkg : mAncestralPackages) { + pw.println(" " + pkg); + } + } + pw.println("Ever backed up: " + mEverStoredApps.size()); for (String pkg : mEverStoredApps) { pw.println(" " + pkg); diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index bb36936..5cf61bd 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -27,14 +27,14 @@ import android.content.pm.PackageManager; import android.os.BatteryManager; import android.os.Binder; import android.os.IBinder; +import android.os.DropBoxManager; 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 android.util.Slog; import java.io.File; import java.io.FileDescriptor; @@ -68,23 +68,19 @@ import java.io.PrintWriter; */ 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 CRITICAL_BATTERY_LEVEL = 4; private static final int DUMP_MAX_LENGTH = 24 * 1024; private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "-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 @@ -92,7 +88,7 @@ class BatteryService extends Binder { private final Context mContext; private final IBatteryStats mBatteryStats; - + private boolean mAcOnline; private boolean mUsbOnline; private int mBatteryStatus; @@ -117,12 +113,12 @@ class BatteryService extends Binder { private int mPlugType; private int mLastPlugType = -1; // Extra state so we can detect first run - + private long mDischargeStartTime; private int mDischargeStartLevel; - + private boolean mSentLowBatteryBroadcast = false; - + public BatteryService(Context context) { mContext = context; mBatteryStats = BatteryStatsService.getService(); @@ -181,6 +177,7 @@ class BatteryService extends Binder { void systemReady() { // check our power situation now that it is safe to display the shutdown dialog. shutdownIfNoPower(); + shutdownIfOverTemp(); } private final void shutdownIfNoPower() { @@ -194,6 +191,17 @@ class BatteryService extends Binder { } } + private final void shutdownIfOverTemp() { + // shut down gracefully if temperature is too high (> 68.0C) + // wait until the system has booted before attempting to display the shutdown dialog. + if (mBatteryTemperature > 680 && ActivityManagerNative.isSystemReady()) { + Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); + intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + } + private native void native_update(); private synchronized final void update() { @@ -203,6 +211,7 @@ class BatteryService extends Binder { long dischargeDuration = 0; shutdownIfNoPower(); + shutdownIfOverTemp(); mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL; if (mAcOnline) { @@ -219,20 +228,20 @@ class BatteryService extends Binder { mPlugType != mLastPlugType || 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) { dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime; logOutlier = true; - EventLog.writeEvent(LOG_BATTERY_DISCHARGE_STATUS, dischargeDuration, + EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration, mDischargeStartLevel, mBatteryLevel); // make sure we see a discharge event before logging again - mDischargeStartTime = 0; + mDischargeStartTime = 0; } } else if (mPlugType == BATTERY_PLUGGED_NONE) { // charging -> discharging or we just powered up @@ -244,19 +253,19 @@ class BatteryService extends Binder { mBatteryHealth != mLastBatteryHealth || mBatteryPresent != mLastBatteryPresent || mPlugType != mLastPlugType) { - EventLog.writeEvent(LOG_BATTERY_STATUS, + EventLog.writeEvent(EventLogTags.BATTERY_STATUS, mBatteryStatus, mBatteryHealth, mBatteryPresent ? 1 : 0, mPlugType, mBatteryTechnology); } if (mBatteryLevel != mLastBatteryLevel || mBatteryVoltage != mLastBatteryVoltage || mBatteryTemperature != mLastBatteryTemperature) { - EventLog.writeEvent(LOG_BATTERY_LEVEL, + EventLog.writeEvent(EventLogTags.BATTERY_LEVEL, mBatteryLevel, mBatteryVoltage, mBatteryTemperature); } if (mBatteryLevel != mLastBatteryLevel && mPlugType == BATTERY_PLUGGED_NONE) { // If the battery level has changed and we are on battery, update the current level. - // This is used for discharge cycle tracking so this shouldn't be updated while the + // This is used for discharge cycle tracking so this shouldn't be updated while the // battery is charging. try { mBatteryStats.recordCurrentLevel(mBatteryLevel); @@ -271,7 +280,7 @@ class BatteryService extends Binder { dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime; logOutlier = true; } - + final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE; final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE; @@ -285,9 +294,9 @@ class BatteryService extends Binder { && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN && mBatteryLevel <= mLowBatteryWarningLevel && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel); - + sendIntent(); - + // Separate broadcast is sent for power connected / not connected // since the standard intent will not wake any applications and some // applications may want to have smart behavior based on this. @@ -311,12 +320,12 @@ class BatteryService extends Binder { statusIntent.setAction(Intent.ACTION_BATTERY_OKAY); mContext.sendBroadcast(statusIntent); } - + // This needs to be done after sendIntent() so that we get the lastest battery stats. if (logOutlier && dischargeDuration != 0) { logOutlier(dischargeDuration); } - + mLastBatteryStatus = mBatteryStatus; mLastBatteryHealth = mBatteryHealth; mLastBatteryPresent = mBatteryPresent; @@ -331,13 +340,14 @@ class BatteryService extends Binder { private final void sendIntent() { // Pack up the values and broadcast them to everyone Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_REPLACE_PENDING); try { mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE, mBatteryLevel); } catch (RemoteException e) { // Should never happen. } - + int icon = getIcon(mBatteryLevel); intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryStatus); @@ -352,9 +362,9 @@ class BatteryService extends Binder { intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology); if (false) { - Log.d(TAG, "updateBattery level:" + mBatteryLevel + - " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus + - " health:" + mBatteryHealth + " present:" + mBatteryPresent + + Slog.d(TAG, "updateBattery level:" + mBatteryLevel + + " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus + + " health:" + mBatteryHealth + " present:" + mBatteryPresent + " voltage: " + mBatteryVoltage + " temperature: " + mBatteryTemperature + " technology: " + mBatteryTechnology + @@ -366,76 +376,65 @@ class BatteryService extends Binder { } 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()); + if (batteryInfoService == null) return; + + DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE); + if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return; + + 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(); + + // add dump file to drop box + db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT); + } catch (RemoteException e) { + Slog.e(TAG, "failed to dump battery service", e); + } catch (IOException e) { + Slog.e(TAG, "failed to write dumpsys file", e); + } finally { + // make sure we clean up + if (dumpStream != null) { + try { + dumpStream.close(); + } catch (IOException e) { + Slog.e(TAG, "failed to close dumpsys output stream"); } } + if (dumpFile != null && !dumpFile.delete()) { + Slog.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); - + String dischargeThresholdString = Settings.Secure.getString(cr, + Settings.Secure.BATTERY_DISCHARGE_THRESHOLD); + String durationThresholdString = Settings.Secure.getString(cr, + Settings.Secure.BATTERY_DISCHARGE_DURATION_THRESHOLD); + if (dischargeThresholdString != null && durationThresholdString != null) { try { long durationThreshold = Long.parseLong(durationThresholdString); int dischargeThreshold = Integer.parseInt(dischargeThresholdString); - if (duration <= durationThreshold && + 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 + + if (LOCAL_LOGV) Slog.v(TAG, "duration threshold: " + durationThreshold + " discharge threshold: " + dischargeThreshold); - if (LOCAL_LOGV) Log.v(TAG, "duration: " + duration + " discharge: " + + if (LOCAL_LOGV) Slog.v(TAG, "duration: " + duration + " discharge: " + (mDischargeStartLevel - mBatteryLevel)); } catch (NumberFormatException e) { - Log.e(TAG, "Invalid DischargeThresholds GService string: " + + Slog.e(TAG, "Invalid DischargeThresholds GService string: " + durationThresholdString + " or " + dischargeThresholdString); return; } @@ -458,7 +457,7 @@ class BatteryService extends Binder { 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 Battery service from from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); diff --git a/services/java/com/android/server/BootReceiver.java b/services/java/com/android/server/BootReceiver.java index 590b1e4..f409751 100644 --- a/services/java/com/android/server/BootReceiver.java +++ b/services/java/com/android/server/BootReceiver.java @@ -1,40 +1,174 @@ /* -** -** Copyright 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. -*/ + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.server; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.BroadcastReceiver; +import android.content.SharedPreferences; +import android.net.Downloads; +import android.os.Build; +import android.os.DropBoxManager; +import android.os.FileObserver; +import android.os.FileUtils; +import android.os.RecoverySystem; +import android.os.SystemProperties; import android.provider.Settings; +import android.util.Slog; + +import java.io.File; +import java.io.IOException; + +/** + * Performs a number of miscellaneous, non-system-critical actions + * after the system has finished booting. + */ +public class BootReceiver extends BroadcastReceiver { + private static final String TAG = "BootReceiver"; + + // Maximum size of a logged event (files get truncated if they're longer) + private static final int LOG_SIZE = 65536; + + private static final File TOMBSTONE_DIR = new File("/data/tombstones"); + + // The pre-froyo package and class of the system updater, which + // ran in the system process. We need to remove its packages here + // in order to clean up after a pre-froyo-to-froyo update. + private static final String OLD_UPDATER_PACKAGE = + "com.google.android.systemupdater"; + private static final String OLD_UPDATER_CLASS = + "com.google.android.systemupdater.SystemUpdateReceiver"; + + // Keep a reference to the observer so the finalizer doesn't disable it. + private static FileObserver sTombstoneObserver = null; -public class BootReceiver extends BroadcastReceiver -{ @Override - public void onReceive(Context context, Intent intent) - { - Intent service = new Intent(context, com.android.server.LoadAverageService.class); - ContentResolver res = context.getContentResolver(); - boolean shown = Settings.System.getInt( - res, Settings.System.SHOW_PROCESSES, 0) != 0; - if (shown) { - context.startService(service); + public void onReceive(final Context context, Intent intent) { + try { + // Start the load average overlay, if activated + ContentResolver res = context.getContentResolver(); + if (Settings.System.getInt(res, Settings.System.SHOW_PROCESSES, 0) != 0) { + Intent loadavg = new Intent(context, com.android.server.LoadAverageService.class); + context.startService(loadavg); + } + } catch (Exception e) { + Slog.e(TAG, "Can't start load average service", e); } + + // Log boot events in the background to avoid blocking the main thread with I/O + new Thread() { + @Override + public void run() { + try { + logBootEvents(context); + } catch (Exception e) { + Slog.e(TAG, "Can't log boot events", e); + } + try { + removeOldUpdatePackages(context); + } catch (Exception e) { + Slog.e(TAG, "Can't remove old update packages", e); + } + + } + }.start(); } -} + private void removeOldUpdatePackages(Context ctx) { + Downloads.ByUri.removeAllDownloadsByPackage( + ctx, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS); + } + + private void logBootEvents(Context ctx) throws IOException { + final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE); + final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE); + final String headers = new StringBuilder(512) + .append("Build: ").append(Build.FINGERPRINT).append("\n") + .append("Hardware: ").append(Build.BOARD).append("\n") + .append("Bootloader: ").append(Build.BOOTLOADER).append("\n") + .append("Radio: ").append(Build.RADIO).append("\n") + .append("Kernel: ") + .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n")) + .append("\n").toString(); + + String recovery = RecoverySystem.handleAftermath(); + if (recovery != null && db != null) { + db.addText("SYSTEM_RECOVERY_LOG", headers + recovery); + } + + if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) { + String now = Long.toString(System.currentTimeMillis()); + SystemProperties.set("ro.runtime.firstboot", now); + if (db != null) db.addText("SYSTEM_BOOT", headers); + + // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile()) + addFileToDropBox(db, prefs, headers, "/proc/last_kmsg", + -LOG_SIZE, "SYSTEM_LAST_KMSG"); + addFileToDropBox(db, prefs, headers, "/cache/recovery/log", + -LOG_SIZE, "SYSTEM_RECOVERY_LOG"); + addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console", + -LOG_SIZE, "APANIC_CONSOLE"); + addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads", + -LOG_SIZE, "APANIC_THREADS"); + } else { + if (db != null) db.addText("SYSTEM_RESTART", headers); + } + + // Scan existing tombstones (in case any new ones appeared) + File[] tombstoneFiles = TOMBSTONE_DIR.listFiles(); + for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) { + addFileToDropBox(db, prefs, headers, tombstoneFiles[i].getPath(), + LOG_SIZE, "SYSTEM_TOMBSTONE"); + } + + // Start watching for new tombstone files; will record them as they occur. + // This gets registered with the singleton file observer thread. + sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) { + @Override + public void onEvent(int event, String path) { + try { + String filename = new File(TOMBSTONE_DIR, path).getPath(); + addFileToDropBox(db, prefs, headers, filename, LOG_SIZE, "SYSTEM_TOMBSTONE"); + } catch (IOException e) { + Slog.e(TAG, "Can't log tombstone", e); + } + } + }; + + sTombstoneObserver.startWatching(); + } + + private static void addFileToDropBox( + DropBoxManager db, SharedPreferences prefs, + String headers, String filename, int maxSize, String tag) throws IOException { + if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled + + File file = new File(filename); + long fileTime = file.lastModified(); + if (fileTime <= 0) return; // File does not exist + + if (prefs != null) { + long lastTime = prefs.getLong(filename, 0); + if (lastTime == fileTime) return; // Already logged this particular file + prefs.edit().putLong(filename, fileTime).commit(); + } + + Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")"); + db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n")); + } +} diff --git a/services/java/com/android/server/BrickReceiver.java b/services/java/com/android/server/BrickReceiver.java index 6c4db0d..cff3805 100644 --- a/services/java/com/android/server/BrickReceiver.java +++ b/services/java/com/android/server/BrickReceiver.java @@ -20,12 +20,12 @@ import android.content.Context; import android.content.Intent; import android.content.BroadcastReceiver; import android.os.SystemService; -import android.util.Log; +import android.util.Slog; public class BrickReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - Log.w("BrickReceiver", "!!! BRICKING DEVICE !!!"); + Slog.w("BrickReceiver", "!!! BRICKING DEVICE !!!"); SystemService.start("brick"); } } diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 3d7025d..81b8d40 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -39,10 +39,12 @@ import android.os.SystemProperties; import android.provider.Settings; import android.text.TextUtils; import android.util.EventLog; -import android.util.Log; +import android.util.Slog; import com.android.internal.telephony.Phone; +import com.android.server.connectivity.Tethering; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -56,15 +58,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final boolean DBG = true; private static final String TAG = "ConnectivityService"; - // Event log tags (must be in sync with event-log-tags) - private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020; - // how long to wait before switching back to a radio's default network private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000; // system property that can override the above value private static final String NETWORK_RESTORE_DELAY_PROP_NAME = "android.telephony.apn-restore"; + + private Tethering mTethering; + private boolean mTetheringConfigValid = false; + /** * Sometimes we want to refer to the individual network state * trackers separately, and sometimes we just want to treat them @@ -78,8 +81,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private List mNetRequestersPids[]; - private WifiWatchdogService mWifiWatchdogService; - // priority order of the nettrackers // (excluding dynamically set mNetworkPreference) // TODO - move mNetworkTypePreference into this @@ -101,9 +102,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { private List mFeatureUsers; private boolean mSystemReady; - private ArrayList<Intent> mDeferredBroadcasts; + private Intent mInitialBroadcast; - private class NetworkAttributes { + private static class NetworkAttributes { /** * Class for holding settings read from resources. */ @@ -115,23 +116,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { public NetworkAttributes(String init) { String fragments[] = init.split(","); mName = fragments[0].toLowerCase(); - if (fragments[1].toLowerCase().equals("wifi")) { - mRadio = ConnectivityManager.TYPE_WIFI; - } else { - mRadio = ConnectivityManager.TYPE_MOBILE; - } - if (mName.equals("default")) { - mType = mRadio; - } else if (mName.equals("mms")) { - mType = ConnectivityManager.TYPE_MOBILE_MMS; - } else if (mName.equals("supl")) { - mType = ConnectivityManager.TYPE_MOBILE_SUPL; - } else if (mName.equals("dun")) { - mType = ConnectivityManager.TYPE_MOBILE_DUN; - } else if (mName.equals("hipri")) { - mType = ConnectivityManager.TYPE_MOBILE_HIPRI; - } - mPriority = Integer.parseInt(fragments[2]); + mType = Integer.parseInt(fragments[1]); + mRadio = Integer.parseInt(fragments[2]); + mPriority = Integer.parseInt(fragments[3]); mLastState = NetworkInfo.State.UNKNOWN; } public boolean isDefault() { @@ -139,22 +126,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } NetworkAttributes[] mNetAttributes; + int mNetworksDefined; - private class RadioAttributes { - public String mName; - public int mPriority; + private static class RadioAttributes { public int mSimultaneity; public int mType; public RadioAttributes(String init) { String fragments[] = init.split(","); - mName = fragments[0].toLowerCase(); - mPriority = Integer.parseInt(fragments[1]); - mSimultaneity = Integer.parseInt(fragments[2]); - if (mName.equals("wifi")) { - mType = ConnectivityManager.TYPE_WIFI; - } else { - mType = ConnectivityManager.TYPE_MOBILE; - } + mType = Integer.parseInt(fragments[0]); + mSimultaneity = Integer.parseInt(fragments[1]); } } RadioAttributes[] mRadioAttributes; @@ -187,7 +167,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { // Wait until sServiceInstance has been initialized. thread.wait(); } catch (InterruptedException ignore) { - Log.e(TAG, + Slog.e(TAG, "Unexpected InterruptedException while waiting"+ " for ConnectivityService thread"); } @@ -203,7 +183,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { } private ConnectivityService(Context context) { - if (DBG) Log.v(TAG, "ConnectivityService starting up"); + if (DBG) Slog.v(TAG, "ConnectivityService starting up"); + + // setup our unique device name + String id = Settings.Secure.getString(context.getContentResolver(), + Settings.Secure.ANDROID_ID); + if (id != null && id.length() > 0) { + String name = new String("android_").concat(id); + SystemProperties.set("net.hostname", name); + } + mContext = context; mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1]; @@ -211,48 +200,87 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetworkPreference = getPersistedNetworkPreference(); + mRadioAttributes = new RadioAttributes[ConnectivityManager.MAX_RADIO_TYPE+1]; + mNetAttributes = new NetworkAttributes[ConnectivityManager.MAX_NETWORK_TYPE+1]; + // Load device network attributes from resources - mNetAttributes = new NetworkAttributes[ - ConnectivityManager.MAX_NETWORK_TYPE+1]; - mRadioAttributes = new RadioAttributes[ - ConnectivityManager.MAX_RADIO_TYPE+1]; - String[] naStrings = context.getResources().getStringArray( - com.android.internal.R.array.networkAttributes); - // TODO - what if the setting has gaps/unknown types? - for (String a : naStrings) { - NetworkAttributes n = new NetworkAttributes(a); - mNetAttributes[n.mType] = n; - } String[] raStrings = context.getResources().getStringArray( com.android.internal.R.array.radioAttributes); - for (String a : raStrings) { - RadioAttributes r = new RadioAttributes(a); + for (String raString : raStrings) { + RadioAttributes r = new RadioAttributes(raString); + if (r.mType > ConnectivityManager.MAX_RADIO_TYPE) { + Slog.e(TAG, "Error in radioAttributes - ignoring attempt to define type " + r.mType); + continue; + } + if (mRadioAttributes[r.mType] != null) { + Slog.e(TAG, "Error in radioAttributes - ignoring attempt to redefine type " + + r.mType); + continue; + } mRadioAttributes[r.mType] = r; } + String[] naStrings = context.getResources().getStringArray( + com.android.internal.R.array.networkAttributes); + for (String naString : naStrings) { + try { + NetworkAttributes n = new NetworkAttributes(naString); + if (n.mType > ConnectivityManager.MAX_NETWORK_TYPE) { + Slog.e(TAG, "Error in networkAttributes - ignoring attempt to define type " + + n.mType); + continue; + } + if (mNetAttributes[n.mType] != null) { + Slog.e(TAG, "Error in networkAttributes - ignoring attempt to redefine type " + + n.mType); + continue; + } + if (mRadioAttributes[n.mRadio] == null) { + Slog.e(TAG, "Error in networkAttributes - ignoring attempt to use undefined " + + "radio " + n.mRadio + " in network type " + n.mType); + continue; + } + mNetAttributes[n.mType] = n; + mNetworksDefined++; + } catch(Exception e) { + // ignore it - leave the entry null + } + } + // high priority first - mPriorityList = new int[naStrings.length]; + mPriorityList = new int[mNetworksDefined]; { - int priority = 0; //lowest - int nextPos = naStrings.length-1; - while (nextPos>-1) { - for (int i = 0; i < mNetAttributes.length; i++) { - if(mNetAttributes[i].mPriority == priority) { - mPriorityList[nextPos--] = i; + int insertionPoint = mNetworksDefined-1; + int currentLowest = 0; + int nextLowest = 0; + while (insertionPoint > -1) { + for (NetworkAttributes na : mNetAttributes) { + if (na == null) continue; + if (na.mPriority < currentLowest) continue; + if (na.mPriority > currentLowest) { + if (na.mPriority < nextLowest || nextLowest == 0) { + nextLowest = na.mPriority; + } + continue; } + mPriorityList[insertionPoint--] = na.mType; } - priority++; + currentLowest = nextLowest; + nextLowest = 0; } } - mNetRequestersPids = - new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1]; - for (int i=0; i<=ConnectivityManager.MAX_NETWORK_TYPE; i++) { + mNetRequestersPids = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1]; + for (int i : mPriorityList) { mNetRequestersPids[i] = new ArrayList(); } mFeatureUsers = new ArrayList(); + mNumDnsEntries = 0; + + mTestMode = SystemProperties.get("cm.test.mode").equals("true") + && SystemProperties.get("ro.build.type").equals("eng"); /* * Create the network state trackers for Wi-Fi and mobile * data. Maybe this could be done with a factory class, @@ -260,49 +288,45 @@ public class ConnectivityService extends IConnectivityManager.Stub { * the number of different network types is not going * to change very often. */ - if (DBG) Log.v(TAG, "Starting Wifi Service."); - WifiStateTracker wst = new WifiStateTracker(context, mHandler); - WifiService wifiService = new WifiService(context, wst); - ServiceManager.addService(Context.WIFI_SERVICE, wifiService); - mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst; - - mNetTrackers[ConnectivityManager.TYPE_MOBILE] = - new MobileDataStateTracker(context, mHandler, - ConnectivityManager.TYPE_MOBILE, Phone.APN_TYPE_DEFAULT, - "MOBILE"); - - mNetTrackers[ConnectivityManager.TYPE_MOBILE_MMS] = - new MobileDataStateTracker(context, mHandler, - ConnectivityManager.TYPE_MOBILE_MMS, Phone.APN_TYPE_MMS, - "MOBILE_MMS"); - - mNetTrackers[ConnectivityManager.TYPE_MOBILE_SUPL] = - new MobileDataStateTracker(context, mHandler, - ConnectivityManager.TYPE_MOBILE_SUPL, Phone.APN_TYPE_SUPL, - "MOBILE_SUPL"); - - mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] = - new MobileDataStateTracker(context, mHandler, - ConnectivityManager.TYPE_MOBILE_DUN, Phone.APN_TYPE_DUN, - "MOBILE_DUN"); - - mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI] = - new MobileDataStateTracker(context, mHandler, - ConnectivityManager.TYPE_MOBILE_HIPRI, Phone.APN_TYPE_HIPRI, - "MOBILE_HIPRI"); - - mNumDnsEntries = 0; - - mTestMode = SystemProperties.get("cm.test.mode").equals("true") - && SystemProperties.get("ro.build.type").equals("eng"); + boolean noMobileData = !getMobileDataEnabled(); + for (int netType : mPriorityList) { + switch (mNetAttributes[netType].mRadio) { + case ConnectivityManager.TYPE_WIFI: + if (DBG) Slog.v(TAG, "Starting Wifi Service."); + WifiStateTracker wst = new WifiStateTracker(context, mHandler); + WifiService wifiService = new WifiService(context, wst); + ServiceManager.addService(Context.WIFI_SERVICE, wifiService); + wifiService.startWifi(); + mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst; + wst.startMonitoring(); + + break; + case ConnectivityManager.TYPE_MOBILE: + mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler, + netType, mNetAttributes[netType].mName); + mNetTrackers[netType].startMonitoring(); + if (noMobileData) { + if (DBG) Slog.d(TAG, "tearing down Mobile networks due to setting"); + mNetTrackers[netType].teardown(); + } + break; + default: + Slog.e(TAG, "Trying to create a DataStateTracker for an unknown radio type " + + mNetAttributes[netType].mRadio); + continue; + } + } - for (NetworkStateTracker t : mNetTrackers) - t.startMonitoring(); + mTethering = new Tethering(mContext, mHandler.getLooper()); + mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) || + !mTethering.isDunRequired()) && + (mTethering.getTetherableUsbRegexs().length != 0 || + mTethering.getTetherableWifiRegexs().length != 0) && + mTethering.getUpstreamIfaceRegexs().length != 0); - // Constructing this starts it too - mWifiWatchdogService = new WifiWatchdogService(context, wst); } + /** * Sets the preferred network. * @param preference the new preference @@ -310,6 +334,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { public synchronized void setNetworkPreference(int preference) { enforceChangePermission(); if (ConnectivityManager.isNetworkTypeValid(preference) && + mNetAttributes[preference] != null && mNetAttributes[preference].isDefault()) { if (mNetworkPreference != preference) { persistNetworkPreference(preference); @@ -357,10 +382,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { return; for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) { - if (t != mNetworkPreference && + if (t != mNetworkPreference && mNetTrackers[t] != null && mNetTrackers[t].getNetworkInfo().isConnected()) { if (DBG) { - Log.d(TAG, "tearing down " + + Slog.d(TAG, "tearing down " + mNetTrackers[t].getNetworkInfo() + " in enforcePreference"); } @@ -388,13 +413,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { public NetworkInfo getActiveNetworkInfo() { enforceAccessPermission(); for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) { - if (!mNetAttributes[type].isDefault()) { + if (mNetAttributes[type] == null || !mNetAttributes[type].isDefault()) { continue; } NetworkStateTracker t = mNetTrackers[type]; NetworkInfo info = t.getNetworkInfo(); if (info.isConnected()) { - if (DBG && type != mActiveDefaultNetwork) Log.e(TAG, + if (DBG && type != mActiveDefaultNetwork) Slog.e(TAG, "connected default network is not " + "mActiveDefaultNetwork!"); return info; @@ -415,10 +440,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { public NetworkInfo[] getAllNetworkInfo() { enforceAccessPermission(); - NetworkInfo[] result = new NetworkInfo[mNetTrackers.length]; + NetworkInfo[] result = new NetworkInfo[mNetworksDefined]; int i = 0; for (NetworkStateTracker t : mNetTrackers) { - result[i++] = t.getNetworkInfo(); + if(t != null) result[i++] = t.getNetworkInfo(); } return result; } @@ -427,7 +452,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { boolean result = true; enforceChangePermission(); for (NetworkStateTracker t : mNetTrackers) { - result = t.setRadio(turnOn) && result; + if (t != null) result = t.setRadio(turnOn) && result; } return result; } @@ -477,14 +502,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { } public void binderDied() { - Log.d(TAG, "ConnectivityService FeatureUser binderDied(" + + Slog.d(TAG, "ConnectivityService FeatureUser binderDied(" + mNetworkType + ", " + mFeature + ", " + mBinder + "), created " + (System.currentTimeMillis() - mCreateTime) + " mSec ago"); stopUsingNetworkFeature(this, false); } public void expire() { - Log.d(TAG, "ConnectivityService FeatureUser expire(" + + Slog.d(TAG, "ConnectivityService FeatureUser expire(" + mNetworkType + ", " + mFeature + ", " + mBinder +"), created " + (System.currentTimeMillis() - mCreateTime) + " mSec ago"); stopUsingNetworkFeature(this, false); @@ -500,11 +525,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { public int startUsingNetworkFeature(int networkType, String feature, IBinder binder) { if (DBG) { - Log.d(TAG, "startUsingNetworkFeature for net " + networkType + + Slog.d(TAG, "startUsingNetworkFeature for net " + networkType + ": " + feature); } enforceChangePermission(); - if (!ConnectivityManager.isNetworkTypeValid(networkType)) { + if (!ConnectivityManager.isNetworkTypeValid(networkType) || + mNetAttributes[networkType] == null) { return Phone.APN_REQUEST_FAILED; } @@ -513,6 +539,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { // TODO - move this into the MobileDataStateTracker int usedNetworkType = networkType; if(networkType == ConnectivityManager.TYPE_MOBILE) { + if (!getMobileDataEnabled()) { + if (DBG) Slog.d(TAG, "requested special network with data disabled - rejected"); + return Phone.APN_TYPE_NOT_AVAILABLE; + } if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS; } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { @@ -532,7 +562,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkInfo ni = network.getNetworkInfo(); if (ni.isAvailable() == false) { - if (DBG) Log.d(TAG, "special network not available"); + if (DBG) Slog.d(TAG, "special network not available"); return Phone.APN_TYPE_NOT_AVAILABLE; } @@ -553,17 +583,17 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (ni.isConnected() == true) { // add the pid-specific dns handleDnsConfigurationChange(); - if (DBG) Log.d(TAG, "special network already active"); + if (DBG) Slog.d(TAG, "special network already active"); return Phone.APN_ALREADY_ACTIVE; } - if (DBG) Log.d(TAG, "special network already connecting"); + if (DBG) Slog.d(TAG, "special network already connecting"); return Phone.APN_REQUEST_STARTED; } // check if the radio in play can make another contact // assume if cannot for now - if (DBG) Log.d(TAG, "reconnecting to special network"); + if (DBG) Slog.d(TAG, "reconnecting to special network"); network.reconnect(); return Phone.APN_REQUEST_STARTED; } else { @@ -583,6 +613,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { // javadoc from interface public int stopUsingNetworkFeature(int networkType, String feature) { + enforceChangePermission(); + int pid = getCallingPid(); int uid = getCallingUid(); @@ -605,7 +637,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { return stopUsingNetworkFeature(u, true); } else { // none found! - if (DBG) Log.d(TAG, "ignoring stopUsingNetworkFeature - not a live request"); + if (DBG) Slog.d(TAG, "ignoring stopUsingNetworkFeature - not a live request"); return 1; } } @@ -620,10 +652,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { boolean callTeardown = false; // used to carry our decision outside of sync block if (DBG) { - Log.d(TAG, "stopUsingNetworkFeature for net " + networkType + + Slog.d(TAG, "stopUsingNetworkFeature for net " + networkType + ": " + feature); } - enforceChangePermission(); + if (!ConnectivityManager.isNetworkTypeValid(networkType)) { return -1; } @@ -633,6 +665,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { synchronized(this) { // check if this process still has an outstanding start request if (!mFeatureUsers.contains(u)) { + if (DBG) Slog.d(TAG, "ignoring - this process has no outstanding requests"); return 1; } u.unlinkDeathRecipient(); @@ -650,7 +683,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (x.mUid == u.mUid && x.mPid == u.mPid && x.mNetworkType == u.mNetworkType && TextUtils.equals(x.mFeature, u.mFeature)) { - if (DBG) Log.d(TAG, "ignoring stopUsingNetworkFeature as dup is found"); + if (DBG) Slog.d(TAG, "ignoring stopUsingNetworkFeature as dup is found"); return 1; } } @@ -670,19 +703,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } tracker = mNetTrackers[usedNetworkType]; - if(usedNetworkType != networkType) { + if (tracker == null) { + if (DBG) Slog.d(TAG, "ignoring - no known tracker for net type " + usedNetworkType); + return -1; + } + if (usedNetworkType != networkType) { Integer currentPid = new Integer(pid); mNetRequestersPids[usedNetworkType].remove(currentPid); reassessPidDns(pid, true); if (mNetRequestersPids[usedNetworkType].size() != 0) { - if (DBG) Log.d(TAG, "not tearing down special network - " + + if (DBG) Slog.d(TAG, "not tearing down special network - " + "others still using it"); return 1; } callTeardown = true; } } - + if (DBG) Slog.d(TAG, "Doing network teardown"); if (callTeardown) { tracker.teardown(); return 1; @@ -708,9 +745,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { } NetworkStateTracker tracker = mNetTrackers[networkType]; - if (!tracker.getNetworkInfo().isConnected() || tracker.isTeardownRequested()) { + if (tracker == null || !tracker.getNetworkInfo().isConnected() || + tracker.isTeardownRequested()) { if (DBG) { - Log.d(TAG, "requestRouteToHost on down network (" + networkType + " - dropped"); + Slog.d(TAG, "requestRouteToHost on down network (" + networkType + ") - dropped"); } return false; } @@ -744,11 +782,51 @@ public class ConnectivityService extends IConnectivityManager.Stub { mContext.sendBroadcast(broadcast); } + /** + * @see ConnectivityManager#getMobileDataEnabled() + */ + public boolean getMobileDataEnabled() { + enforceAccessPermission(); + boolean retVal = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.MOBILE_DATA, 1) == 1; + if (DBG) Slog.d(TAG, "getMobileDataEnabled returning " + retVal); + return retVal; + } + + /** + * @see ConnectivityManager#setMobileDataEnabled(boolean) + */ + public synchronized void setMobileDataEnabled(boolean enabled) { + enforceChangePermission(); + if (DBG) Slog.d(TAG, "setMobileDataEnabled(" + enabled + ")"); + + if (getMobileDataEnabled() == enabled) return; + + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.MOBILE_DATA, enabled ? 1 : 0); + + if (enabled) { + if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) { + if (DBG) Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]); + mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect(); + } + } else { + for (NetworkStateTracker nt : mNetTrackers) { + if (nt == null) continue; + int netType = nt.getNetworkInfo().getType(); + if (mNetAttributes[netType].mRadio == ConnectivityManager.TYPE_MOBILE) { + if (DBG) Slog.d(TAG, "tearing down " + nt); + nt.teardown(); + } + } + } + } + private int getNumConnectedNetworks() { int numConnectedNets = 0; for (NetworkStateTracker nt : mNetTrackers) { - if (nt.getNetworkInfo().isConnected() && + if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) { ++numConnectedNets; } @@ -768,6 +846,19 @@ public class ConnectivityService extends IConnectivityManager.Stub { "ConnectivityService"); } + // TODO Make this a special check when it goes public + private void enforceTetherChangePermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, + "ConnectivityService"); + } + + private void enforceTetherAccessPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, + "ConnectivityService"); + } + /** * Handle a {@code DISCONNECTED} event. If this pertains to the non-active * network, we ignore it. If it is for the active network, we send out a @@ -798,6 +889,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); if (info.isFailover()) { intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); @@ -848,8 +940,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { int newType = -1; int newPriority = -1; + boolean noMobileData = !getMobileDataEnabled(); for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) { - if (checkType == prevNetType) { + if (checkType == prevNetType) continue; + if (mNetAttributes[checkType] == null) continue; + if (mNetAttributes[checkType].mRadio == ConnectivityManager.TYPE_MOBILE && + noMobileData) { + if (DBG) { + Slog.d(TAG, "not failing over to mobile type " + checkType + + " because Mobile Data Disabled"); + } continue; } if (mNetAttributes[checkType].isDefault()) { @@ -860,10 +960,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { newType = checkType; break; } - if (mRadioAttributes[mNetAttributes[checkType].mRadio]. - mPriority > newPriority) { + if (mNetAttributes[checkType].mPriority > newPriority) { newType = checkType; - newPriority = mRadioAttributes[mNetAttributes[newType].mRadio].mPriority; + newPriority = mNetAttributes[newType].mPriority; } } } @@ -886,15 +985,17 @@ public class ConnectivityService extends IConnectivityManager.Stub { } if (DBG) { if (switchTo.isConnected()) { - Log.v(TAG, "Switching to already connected " + + Slog.v(TAG, "Switching to already connected " + switchTo.getTypeName()); } else { - Log.v(TAG, "Attempting to switch to " + + Slog.v(TAG, "Attempting to switch to " + switchTo.getTypeName()); } } } else { newNet.reconnect(); + newNet = null; // not officially avail.. try anyway, but + // report no failover } } } @@ -904,6 +1005,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private void sendConnectedBroadcast(NetworkInfo info) { Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); if (info.isFailover()) { intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); @@ -936,11 +1038,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { } else { reasonText = " (" + reason + ")."; } - Log.v(TAG, "Attempt to connect to " + info.getTypeName() + + Slog.v(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText); } Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); if (getActiveNetworkInfo() == null) { intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); @@ -982,26 +1085,20 @@ public class ConnectivityService extends IConnectivityManager.Stub { private void sendStickyBroadcast(Intent intent) { synchronized(this) { - if (mSystemReady) { - mContext.sendStickyBroadcast(intent); - } else { - if (mDeferredBroadcasts == null) { - mDeferredBroadcasts = new ArrayList<Intent>(); - } - mDeferredBroadcasts.add(intent); + if (!mSystemReady) { + mInitialBroadcast = new Intent(intent); } + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendStickyBroadcast(intent); } } void systemReady() { synchronized(this) { mSystemReady = true; - if (mDeferredBroadcasts != null) { - int count = mDeferredBroadcasts.size(); - for (int i = 0; i < count; i++) { - mContext.sendStickyBroadcast(mDeferredBroadcasts.get(i)); - } - mDeferredBroadcasts = null; + if (mInitialBroadcast != null) { + mContext.sendStickyBroadcast(mInitialBroadcast); + mInitialBroadcast = null; } } } @@ -1022,7 +1119,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetAttributes[type].mPriority) || mNetworkPreference == mActiveDefaultNetwork) { // don't accept this one - if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION " + + if (DBG) Slog.v(TAG, "Not broadcasting CONNECT_ACTION " + "to torn down network " + info.getTypeName()); teardown(thisNet); return; @@ -1030,11 +1127,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { // tear down the other NetworkStateTracker otherNet = mNetTrackers[mActiveDefaultNetwork]; - if (DBG) Log.v(TAG, "Policy requires " + + if (DBG) Slog.v(TAG, "Policy requires " + otherNet.getNetworkInfo().getTypeName() + " teardown"); if (!teardown(otherNet)) { - Log.e(TAG, "Network declined teardown request"); + Slog.e(TAG, "Network declined teardown request"); return; } if (isFailover) { @@ -1053,7 +1150,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private void handleScanResultsAvailable(NetworkInfo info) { int networkType = info.getType(); if (networkType != ConnectivityManager.TYPE_WIFI) { - if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + + if (DBG) Slog.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network. Don't know how to handle."); } @@ -1116,7 +1213,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private void reassessPidDns(int myPid, boolean doBump) { - if (DBG) Log.d(TAG, "reassessPidDns for pid " + myPid); + if (DBG) Slog.d(TAG, "reassessPidDns for pid " + myPid); for(int i : mPriorityList) { if (mNetAttributes[i].isDefault()) { continue; @@ -1188,14 +1285,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { for (String dns : dnsList) { if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) { if (DBG) { - Log.d(TAG, "adding dns " + dns + " for " + + Slog.d(TAG, "adding dns " + dns + " for " + nt.getNetworkInfo().getTypeName()); } SystemProperties.set("net.dns" + j++, dns); } } for (int k=j ; k<mNumDnsEntries; k++) { - if (DBG) Log.d(TAG, "erasing net.dns" + k); + if (DBG) Slog.d(TAG, "erasing net.dns" + k); SystemProperties.set("net.dns" + k, ""); } mNumDnsEntries = j; @@ -1264,6 +1361,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { pw.println(requester.toString()); } pw.println(); + + mTethering.dump(fd, pw, args); } // must be stateless - things change under us. @@ -1286,7 +1385,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (DBG) { // TODO - remove this after we validate the dropping doesn't break // anything - Log.d(TAG, "Dropping ConnectivityChange for " + + Slog.d(TAG, "Dropping ConnectivityChange for " + info.getTypeName() + ": " + state + "/" + info.getDetailedState()); } @@ -1294,7 +1393,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } mNetAttributes[type].mLastState = state; - if (DBG) Log.d(TAG, "ConnectivityChange for " + + if (DBG) Slog.d(TAG, "ConnectivityChange for " + info.getTypeName() + ": " + state + "/" + info.getDetailedState()); @@ -1308,7 +1407,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { int eventLogParam = (info.getType() & 0x7) | ((info.getDetailedState().ordinal() & 0x3f) << 3) | (info.getSubtype() << 9); - EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, + EventLog.writeEvent(EventLogTags.CONNECTIVITY_STATE_CHANGED, eventLogParam); if (info.getDetailedState() == @@ -1357,4 +1456,84 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } } + + // javadoc from interface + public int tether(String iface) { + enforceTetherChangePermission(); + + if (isTetheringSupported()) { + return mTethering.tether(iface); + } else { + return ConnectivityManager.TETHER_ERROR_UNSUPPORTED; + } + } + + // javadoc from interface + public int untether(String iface) { + enforceTetherChangePermission(); + + if (isTetheringSupported()) { + return mTethering.untether(iface); + } else { + return ConnectivityManager.TETHER_ERROR_UNSUPPORTED; + } + } + + // javadoc from interface + public int getLastTetherError(String iface) { + enforceTetherAccessPermission(); + + if (isTetheringSupported()) { + return mTethering.getLastTetherError(iface); + } else { + return ConnectivityManager.TETHER_ERROR_UNSUPPORTED; + } + } + + // TODO - proper iface API for selection by property, inspection, etc + public String[] getTetherableUsbRegexs() { + enforceTetherAccessPermission(); + if (isTetheringSupported()) { + return mTethering.getTetherableUsbRegexs(); + } else { + return new String[0]; + } + } + + public String[] getTetherableWifiRegexs() { + enforceTetherAccessPermission(); + if (isTetheringSupported()) { + return mTethering.getTetherableWifiRegexs(); + } else { + return new String[0]; + } + } + + // TODO - move iface listing, queries, etc to new module + // javadoc from interface + public String[] getTetherableIfaces() { + enforceTetherAccessPermission(); + return mTethering.getTetherableIfaces(); + } + + public String[] getTetheredIfaces() { + enforceTetherAccessPermission(); + return mTethering.getTetheredIfaces(); + } + + public String[] getTetheringErroredIfaces() { + enforceTetherAccessPermission(); + return mTethering.getErroredIfaces(); + } + + // if ro.tether.denied = true we default to no tethering + // gservices could set the secure setting to 1 though to enable it on a build where it + // had previously been turned off. + public boolean isTetheringSupported() { + enforceTetherAccessPermission(); + int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1); + boolean tetherEnabledInSettings = (Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.TETHER_SUPPORTED, defaultVal) != 0); + return tetherEnabledInSettings && mTetheringConfigValid; + } } diff --git a/services/java/com/android/server/DemoDataSet.java b/services/java/com/android/server/DemoDataSet.java index 0de7c1e..277985f 100644 --- a/services/java/com/android/server/DemoDataSet.java +++ b/services/java/com/android/server/DemoDataSet.java @@ -27,7 +27,7 @@ import android.provider.Contacts; import android.provider.Settings; import android.provider.MediaStore.Images; import android.util.Config; -import android.util.Log; +import android.util.Slog; import java.io.File; import java.io.FileNotFoundException; @@ -62,7 +62,7 @@ public class DemoDataSet int count = files.length; if (count == 0) { - Log.i(LOG_TAG, "addDefaultImages: no images found!"); + Slog.i(LOG_TAG, "addDefaultImages: no images found!"); return; } @@ -74,14 +74,14 @@ public class DemoDataSet try { Images.Media.insertImage(mContentResolver, path, name, null); } catch (FileNotFoundException e) { - Log.e(LOG_TAG, "Failed to import image " + path, e); + Slog.e(LOG_TAG, "Failed to import image " + path, e); } } } private final void addDefaultData() { - Log.i(LOG_TAG, "Adding default data..."); + Slog.i(LOG_TAG, "Adding default data..."); // addImage("Violet", "images/violet.png"); // addImage("Corky", "images/corky.png"); @@ -124,7 +124,7 @@ public class DemoDataSet } catch (Exception e) { - Log.e(LOG_TAG, "Failed to insert image '" + file + "'", e); + Slog.e(LOG_TAG, "Failed to insert image '" + file + "'", e); url = null; } @@ -133,7 +133,7 @@ public class DemoDataSet private final Uri addShortcut(String shortcut, Intent intent) { - if (Config.LOGV) Log.v(LOG_TAG, "addShortcut: shortcut=" + shortcut + ", intent=" + intent); + if (Config.LOGV) Slog.v(LOG_TAG, "addShortcut: shortcut=" + shortcut + ", intent=" + intent); return Settings.Bookmarks.add(mContentResolver, intent, null, null, shortcut != null ? shortcut.charAt(0) : 0, 0); } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java new file mode 100644 index 0000000..7fb7db0 --- /dev/null +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -0,0 +1,1004 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import com.android.internal.content.PackageMonitor; +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.JournaledFile; +import com.android.internal.util.XmlUtils; +import com.android.internal.widget.LockPatternUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import android.app.Activity; +import android.app.admin.DeviceAdminInfo; +import android.app.admin.DeviceAdminReceiver; +import android.app.admin.DevicePolicyManager; +import android.app.admin.IDevicePolicyManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Binder; +import android.os.IBinder; +import android.os.IPowerManager; +import android.os.RecoverySystem; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.util.Slog; +import android.util.PrintWriterPrinter; +import android.util.Printer; +import android.util.Xml; +import android.view.WindowManagerPolicy; + +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.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Implementation of the device policy APIs. + */ +public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + static final String TAG = "DevicePolicyManagerService"; + + final Context mContext; + final MyPackageMonitor mMonitor; + + IPowerManager mIPowerManager; + + int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + int mActivePasswordLength = 0; + int mFailedPasswordAttempts = 0; + + int mPasswordOwner = -1; + + final HashMap<ComponentName, ActiveAdmin> mAdminMap + = new HashMap<ComponentName, ActiveAdmin>(); + final ArrayList<ActiveAdmin> mAdminList + = new ArrayList<ActiveAdmin>(); + + static class ActiveAdmin { + final DeviceAdminInfo info; + + int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + int minimumPasswordLength = 0; + long maximumTimeToUnlock = 0; + int maximumFailedPasswordsForWipe = 0; + + ActiveAdmin(DeviceAdminInfo _info) { + info = _info; + } + + int getUid() { return info.getActivityInfo().applicationInfo.uid; } + + void writeToXml(XmlSerializer out) + throws IllegalArgumentException, IllegalStateException, IOException { + out.startTag(null, "policies"); + info.writePoliciesToXml(out); + out.endTag(null, "policies"); + if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { + out.startTag(null, "password-quality"); + out.attribute(null, "value", Integer.toString(passwordQuality)); + out.endTag(null, "password-quality"); + if (minimumPasswordLength > 0) { + out.startTag(null, "min-password-length"); + out.attribute(null, "value", Integer.toString(minimumPasswordLength)); + out.endTag(null, "mn-password-length"); + } + } + if (maximumTimeToUnlock != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { + out.startTag(null, "max-time-to-unlock"); + out.attribute(null, "value", Long.toString(maximumTimeToUnlock)); + out.endTag(null, "max-time-to-unlock"); + } + if (maximumFailedPasswordsForWipe != 0) { + out.startTag(null, "max-failed-password-wipe"); + out.attribute(null, "value", Integer.toString(maximumFailedPasswordsForWipe)); + out.endTag(null, "max-failed-password-wipe"); + } + } + + void readFromXml(XmlPullParser parser) + throws XmlPullParserException, IOException { + 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 tag = parser.getName(); + if ("policies".equals(tag)) { + info.readPoliciesFromXml(parser); + } else if ("password-quality".equals(tag)) { + passwordQuality = Integer.parseInt( + parser.getAttributeValue(null, "value")); + } else if ("min-password-length".equals(tag)) { + minimumPasswordLength = Integer.parseInt( + parser.getAttributeValue(null, "value")); + } else if ("max-time-to-unlock".equals(tag)) { + maximumTimeToUnlock = Long.parseLong( + parser.getAttributeValue(null, "value")); + } else if ("max-failed-password-wipe".equals(tag)) { + maximumFailedPasswordsForWipe = Integer.parseInt( + parser.getAttributeValue(null, "value")); + } else { + Slog.w(TAG, "Unknown admin tag: " + tag); + } + XmlUtils.skipCurrentTag(parser); + } + } + + void dump(String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("uid="); pw.println(getUid()); + pw.print(prefix); pw.println("policies:"); + ArrayList<DeviceAdminInfo.PolicyInfo> pols = info.getUsedPolicies(); + if (pols != null) { + for (int i=0; i<pols.size(); i++) { + pw.print(prefix); pw.print(" "); pw.println(pols.get(i).tag); + } + } + pw.print(prefix); pw.print("passwordQuality=0x"); + pw.print(Integer.toHexString(passwordQuality)); + pw.print(" minimumPasswordLength="); + pw.println(minimumPasswordLength); + pw.print(prefix); pw.print("maximumTimeToUnlock="); + pw.println(maximumTimeToUnlock); + pw.print(prefix); pw.print("maximumFailedPasswordsForWipe="); + pw.println(maximumFailedPasswordsForWipe); + } + } + + class MyPackageMonitor extends PackageMonitor { + public void onSomePackagesChanged() { + synchronized (DevicePolicyManagerService.this) { + boolean removed = false; + for (int i=mAdminList.size()-1; i>=0; i--) { + ActiveAdmin aa = mAdminList.get(i); + int change = isPackageDisappearing(aa.info.getPackageName()); + if (change == PACKAGE_PERMANENT_CHANGE + || change == PACKAGE_TEMPORARY_CHANGE) { + Slog.w(TAG, "Admin unexpectedly uninstalled: " + + aa.info.getComponent()); + removed = true; + mAdminList.remove(i); + } else if (isPackageModified(aa.info.getPackageName())) { + try { + mContext.getPackageManager().getReceiverInfo( + aa.info.getComponent(), 0); + } catch (NameNotFoundException e) { + Slog.w(TAG, "Admin package change removed component: " + + aa.info.getComponent()); + removed = true; + mAdminList.remove(i); + } + } + } + if (removed) { + validatePasswordOwnerLocked(); + } + } + } + } + + /** + * Instantiates the service. + */ + public DevicePolicyManagerService(Context context) { + mContext = context; + mMonitor = new MyPackageMonitor(); + mMonitor.register(context, true); + } + + private IPowerManager getIPowerManager() { + if (mIPowerManager == null) { + IBinder b = ServiceManager.getService(Context.POWER_SERVICE); + mIPowerManager = IPowerManager.Stub.asInterface(b); + } + return mIPowerManager; + } + + ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who) { + ActiveAdmin admin = mAdminMap.get(who); + if (admin != null + && who.getPackageName().equals(admin.info.getActivityInfo().packageName) + && who.getClassName().equals(admin.info.getActivityInfo().name)) { + return admin; + } + return null; + } + + ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy) + throws SecurityException { + final int callingUid = Binder.getCallingUid(); + if (who != null) { + ActiveAdmin admin = mAdminMap.get(who); + if (admin == null) { + throw new SecurityException("No active admin " + who); + } + if (admin.getUid() != callingUid) { + throw new SecurityException("Admin " + who + " is not owned by uid " + + Binder.getCallingUid()); + } + if (!admin.info.usesPolicy(reqPolicy)) { + throw new SecurityException("Admin " + admin.info.getComponent() + + " did not specify uses-policy for: " + + admin.info.getTagForPolicy(reqPolicy)); + } + return admin; + } else { + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (admin.getUid() == callingUid && admin.info.usesPolicy(reqPolicy)) { + return admin; + } + } + throw new SecurityException("No active admin owned by uid " + + Binder.getCallingUid() + " for policy #" + reqPolicy); + } + } + + void sendAdminCommandLocked(ActiveAdmin admin, String action) { + Intent intent = new Intent(action); + intent.setComponent(admin.info.getComponent()); + mContext.sendBroadcast(intent); + } + + void sendAdminCommandLocked(String action, int reqPolicy) { + final int N = mAdminList.size(); + if (N > 0) { + for (int i=0; i<N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (admin.info.usesPolicy(reqPolicy)) { + sendAdminCommandLocked(admin, action); + } + } + } + } + + void removeActiveAdminLocked(ComponentName adminReceiver) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver); + if (admin != null) { + sendAdminCommandLocked(admin, + DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED); + // XXX need to wait for it to complete. + mAdminList.remove(admin); + mAdminMap.remove(adminReceiver); + validatePasswordOwnerLocked(); + } + } + + public DeviceAdminInfo findAdmin(ComponentName adminName) { + Intent resolveIntent = new Intent(); + resolveIntent.setComponent(adminName); + List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers( + resolveIntent, PackageManager.GET_META_DATA); + if (infos == null || infos.size() <= 0) { + throw new IllegalArgumentException("Unknown admin: " + adminName); + } + + try { + return new DeviceAdminInfo(mContext, infos.get(0)); + } catch (XmlPullParserException e) { + Slog.w(TAG, "Bad device admin requested: " + adminName, e); + return null; + } catch (IOException e) { + Slog.w(TAG, "Bad device admin requested: " + adminName, e); + return null; + } + } + + private static JournaledFile makeJournaledFile() { + final String base = "/data/system/device_policies.xml"; + return new JournaledFile(new File(base), new File(base + ".tmp")); + } + + private void saveSettingsLocked() { + JournaledFile journal = makeJournaledFile(); + FileOutputStream stream = null; + try { + stream = new FileOutputStream(journal.chooseForWrite(), false); + XmlSerializer out = new FastXmlSerializer(); + out.setOutput(stream, "utf-8"); + out.startDocument(null, true); + + out.startTag(null, "policies"); + + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin ap = mAdminList.get(i); + if (ap != null) { + out.startTag(null, "admin"); + out.attribute(null, "name", ap.info.getComponent().flattenToString()); + ap.writeToXml(out); + out.endTag(null, "admin"); + } + } + + if (mPasswordOwner >= 0) { + out.startTag(null, "password-owner"); + out.attribute(null, "value", Integer.toString(mPasswordOwner)); + out.endTag(null, "password-owner"); + } + + if (mFailedPasswordAttempts != 0) { + out.startTag(null, "failed-password-attempts"); + out.attribute(null, "value", Integer.toString(mFailedPasswordAttempts)); + out.endTag(null, "failed-password-attempts"); + } + + if (mActivePasswordQuality != 0 || mActivePasswordLength != 0) { + out.startTag(null, "active-password"); + out.attribute(null, "quality", Integer.toString(mActivePasswordQuality)); + out.attribute(null, "length", Integer.toString(mActivePasswordLength)); + out.endTag(null, "active-password"); + } + + out.endTag(null, "policies"); + + out.endDocument(); + stream.close(); + journal.commit(); + } catch (IOException e) { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException ex) { + // Ignore + } + journal.rollback(); + } + } + + private void loadSettingsLocked() { + JournaledFile journal = makeJournaledFile(); + FileInputStream stream = null; + File file = journal.chooseForRead(); + try { + stream = new FileInputStream(file); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(stream, null); + + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + } + String tag = parser.getName(); + if (!"policies".equals(tag)) { + throw new XmlPullParserException( + "Settings do not start with policies tag: found " + tag); + } + type = parser.next(); + int outerDepth = parser.getDepth(); + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + tag = parser.getName(); + if ("admin".equals(tag)) { + String name = parser.getAttributeValue(null, "name"); + try { + DeviceAdminInfo dai = findAdmin( + ComponentName.unflattenFromString(name)); + if (dai != null) { + ActiveAdmin ap = new ActiveAdmin(dai); + ap.readFromXml(parser); + mAdminMap.put(ap.info.getComponent(), ap); + mAdminList.add(ap); + } + } catch (RuntimeException e) { + Slog.w(TAG, "Failed loading admin " + name, e); + } + } else if ("failed-password-attempts".equals(tag)) { + mFailedPasswordAttempts = Integer.parseInt( + parser.getAttributeValue(null, "value")); + XmlUtils.skipCurrentTag(parser); + } else if ("password-owner".equals(tag)) { + mPasswordOwner = Integer.parseInt( + parser.getAttributeValue(null, "value")); + XmlUtils.skipCurrentTag(parser); + } else if ("active-password".equals(tag)) { + mActivePasswordQuality = Integer.parseInt( + parser.getAttributeValue(null, "quality")); + mActivePasswordLength = Integer.parseInt( + parser.getAttributeValue(null, "length")); + XmlUtils.skipCurrentTag(parser); + } else { + Slog.w(TAG, "Unknown tag: " + tag); + XmlUtils.skipCurrentTag(parser); + } + } + } catch (NullPointerException e) { + Slog.w(TAG, "failed parsing " + file + " " + e); + } catch (NumberFormatException e) { + Slog.w(TAG, "failed parsing " + file + " " + e); + } catch (XmlPullParserException e) { + Slog.w(TAG, "failed parsing " + file + " " + e); + } catch (IOException e) { + Slog.w(TAG, "failed parsing " + file + " " + e); + } catch (IndexOutOfBoundsException e) { + Slog.w(TAG, "failed parsing " + file + " " + e); + } + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + // Ignore + } + + // Validate that what we stored for the password quality matches + // sufficiently what is currently set. Note that this is only + // a sanity check in case the two get out of sync; this should + // never normally happen. + LockPatternUtils utils = new LockPatternUtils(mContext); + if (utils.getActivePasswordQuality() < mActivePasswordQuality) { + Slog.w(TAG, "Active password quality 0x" + + Integer.toHexString(mActivePasswordQuality) + + " does not match actual quality 0x" + + Integer.toHexString(utils.getActivePasswordQuality())); + mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + mActivePasswordLength = 0; + } + + validatePasswordOwnerLocked(); + + long timeMs = getMaximumTimeToLock(null); + if (timeMs <= 0) { + timeMs = Integer.MAX_VALUE; + } + try { + getIPowerManager().setMaximumScreenOffTimeount((int)timeMs); + } catch (RemoteException e) { + Slog.w(TAG, "Failure talking with power manager", e); + } + } + + static void validateQualityConstant(int quality) { + switch (quality) { + case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: + case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: + case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: + case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: + return; + } + throw new IllegalArgumentException("Invalid quality constant: 0x" + + Integer.toHexString(quality)); + } + + void validatePasswordOwnerLocked() { + if (mPasswordOwner >= 0) { + boolean haveOwner = false; + for (int i=mAdminList.size()-1; i>=0; i--) { + if (mAdminList.get(i).getUid() == mPasswordOwner) { + haveOwner = true; + break; + } + } + if (!haveOwner) { + Slog.w(TAG, "Previous password owner " + mPasswordOwner + + " no longer active; disabling"); + mPasswordOwner = -1; + } + } + } + + public void systemReady() { + synchronized (this) { + loadSettingsLocked(); + } + } + + public void setActiveAdmin(ComponentName adminReceiver) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BIND_DEVICE_ADMIN, null); + + DeviceAdminInfo info = findAdmin(adminReceiver); + if (info == null) { + throw new IllegalArgumentException("Bad admin: " + adminReceiver); + } + synchronized (this) { + long ident = Binder.clearCallingIdentity(); + try { + if (getActiveAdminUncheckedLocked(adminReceiver) != null) { + throw new IllegalArgumentException("Admin is already added"); + } + ActiveAdmin admin = new ActiveAdmin(info); + mAdminMap.put(adminReceiver, admin); + mAdminList.add(admin); + saveSettingsLocked(); + sendAdminCommandLocked(admin, + DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public boolean isAdminActive(ComponentName adminReceiver) { + synchronized (this) { + return getActiveAdminUncheckedLocked(adminReceiver) != null; + } + } + + public List<ComponentName> getActiveAdmins() { + synchronized (this) { + final int N = mAdminList.size(); + if (N <= 0) { + return null; + } + ArrayList<ComponentName> res = new ArrayList<ComponentName>(N); + for (int i=0; i<N; i++) { + res.add(mAdminList.get(i).info.getComponent()); + } + return res; + } + } + + public boolean packageHasActiveAdmins(String packageName) { + synchronized (this) { + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + if (mAdminList.get(i).info.getPackageName().equals(packageName)) { + return true; + } + } + return false; + } + } + + public void removeActiveAdmin(ComponentName adminReceiver) { + synchronized (this) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver); + if (admin == null) { + return; + } + if (admin.getUid() != Binder.getCallingUid()) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BIND_DEVICE_ADMIN, null); + } + long ident = Binder.clearCallingIdentity(); + try { + removeActiveAdminLocked(adminReceiver); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void setPasswordQuality(ComponentName who, int quality) { + validateQualityConstant(quality); + + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); + if (ap.passwordQuality != quality) { + ap.passwordQuality = quality; + saveSettingsLocked(); + } + } + } + + public int getPasswordQuality(ComponentName who) { + synchronized (this) { + int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + + if (who != null) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + return admin != null ? admin.passwordQuality : mode; + } + + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (mode < admin.passwordQuality) { + mode = admin.passwordQuality; + } + } + return mode; + } + } + + public void setPasswordMinimumLength(ComponentName who, int length) { + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); + if (ap.minimumPasswordLength != length) { + ap.minimumPasswordLength = length; + saveSettingsLocked(); + } + } + } + + public int getPasswordMinimumLength(ComponentName who) { + synchronized (this) { + int length = 0; + + if (who != null) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + return admin != null ? admin.minimumPasswordLength : length; + } + + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (length < admin.minimumPasswordLength) { + length = admin.minimumPasswordLength; + } + } + return length; + } + } + + public boolean isActivePasswordSufficient() { + synchronized (this) { + // This API can only be called by an active device admin, + // so try to retrieve it to check that the caller is one. + getActiveAdminForCallerLocked(null, + DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); + return mActivePasswordQuality >= getPasswordQuality(null) + && mActivePasswordLength >= getPasswordMinimumLength(null); + } + } + + public int getCurrentFailedPasswordAttempts() { + synchronized (this) { + // This API can only be called by an active device admin, + // so try to retrieve it to check that the caller is one. + getActiveAdminForCallerLocked(null, + DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); + return mFailedPasswordAttempts; + } + } + + public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) { + synchronized (this) { + // This API can only be called by an active device admin, + // so try to retrieve it to check that the caller is one. + getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_WIPE_DATA); + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); + if (ap.maximumFailedPasswordsForWipe != num) { + ap.maximumFailedPasswordsForWipe = num; + saveSettingsLocked(); + } + } + } + + public int getMaximumFailedPasswordsForWipe(ComponentName who) { + synchronized (this) { + int count = 0; + + if (who != null) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + return admin != null ? admin.maximumFailedPasswordsForWipe : count; + } + + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (count == 0) { + count = admin.maximumFailedPasswordsForWipe; + } else if (admin.maximumFailedPasswordsForWipe != 0 + && count > admin.maximumFailedPasswordsForWipe) { + count = admin.maximumFailedPasswordsForWipe; + } + } + return count; + } + } + + public boolean resetPassword(String password, int flags) { + int quality; + synchronized (this) { + // This API can only be called by an active device admin, + // so try to retrieve it to check that the caller is one. + getActiveAdminForCallerLocked(null, + DeviceAdminInfo.USES_POLICY_RESET_PASSWORD); + quality = getPasswordQuality(null); + if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { + int realQuality = LockPatternUtils.computePasswordQuality(password); + if (realQuality < quality) { + Slog.w(TAG, "resetPassword: password quality 0x" + + Integer.toHexString(quality) + + " does not meet required quality 0x" + + Integer.toHexString(quality)); + return false; + } + quality = realQuality; + } + int length = getPasswordMinimumLength(null); + if (password.length() < length) { + Slog.w(TAG, "resetPassword: password length " + password.length() + + " does not meet required length " + length); + return false; + } + } + + int callingUid = Binder.getCallingUid(); + if (mPasswordOwner >= 0 && mPasswordOwner != callingUid) { + Slog.w(TAG, "resetPassword: already set by another uid and not entered by user"); + return false; + } + + // Don't do this with the lock held, because it is going to call + // back in to the service. + long ident = Binder.clearCallingIdentity(); + try { + LockPatternUtils utils = new LockPatternUtils(mContext); + utils.saveLockPassword(password, quality); + synchronized (this) { + int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) + != 0 ? callingUid : -1; + if (mPasswordOwner != newOwner) { + mPasswordOwner = newOwner; + saveSettingsLocked(); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + + return true; + } + + public void setMaximumTimeToLock(ComponentName who, long timeMs) { + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_FORCE_LOCK); + if (ap.maximumTimeToUnlock != timeMs) { + ap.maximumTimeToUnlock = timeMs; + + long ident = Binder.clearCallingIdentity(); + try { + saveSettingsLocked(); + + timeMs = getMaximumTimeToLock(null); + if (timeMs <= 0) { + timeMs = Integer.MAX_VALUE; + } + + try { + getIPowerManager().setMaximumScreenOffTimeount((int)timeMs); + } catch (RemoteException e) { + Slog.w(TAG, "Failure talking with power manager", e); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + } + + public long getMaximumTimeToLock(ComponentName who) { + synchronized (this) { + long time = 0; + + if (who != null) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + return admin != null ? admin.maximumTimeToUnlock : time; + } + + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (time == 0) { + time = admin.maximumTimeToUnlock; + } else if (admin.maximumTimeToUnlock != 0 + && time > admin.maximumTimeToUnlock) { + time = admin.maximumTimeToUnlock; + } + } + return time; + } + } + + public void lockNow() { + synchronized (this) { + // This API can only be called by an active device admin, + // so try to retrieve it to check that the caller is one. + getActiveAdminForCallerLocked(null, + DeviceAdminInfo.USES_POLICY_FORCE_LOCK); + long ident = Binder.clearCallingIdentity(); + try { + mIPowerManager.goToSleepWithReason(SystemClock.uptimeMillis(), + WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + void wipeDataLocked(int flags) { + try { + RecoverySystem.rebootWipeUserData(mContext); + } catch (IOException e) { + Slog.w(TAG, "Failed requesting data wipe", e); + } + } + + public void wipeData(int flags) { + synchronized (this) { + // This API can only be called by an active device admin, + // so try to retrieve it to check that the caller is one. + getActiveAdminForCallerLocked(null, + DeviceAdminInfo.USES_POLICY_WIPE_DATA); + long ident = Binder.clearCallingIdentity(); + try { + wipeDataLocked(flags); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void getRemoveWarning(ComponentName comp, final RemoteCallback result) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BIND_DEVICE_ADMIN, null); + + synchronized (this) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(comp); + if (admin == null) { + try { + result.sendResult(null); + } catch (RemoteException e) { + } + return; + } + Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED); + intent.setComponent(admin.info.getComponent()); + mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + try { + result.sendResult(getResultExtras(false)); + } catch (RemoteException e) { + } + } + }, null, Activity.RESULT_OK, null, null); + } + } + + public void setActivePasswordState(int quality, int length) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BIND_DEVICE_ADMIN, null); + + validateQualityConstant(quality); + + synchronized (this) { + if (mActivePasswordQuality != quality || mActivePasswordLength != length + || mFailedPasswordAttempts != 0) { + long ident = Binder.clearCallingIdentity(); + try { + mActivePasswordQuality = quality; + mActivePasswordLength = length; + mFailedPasswordAttempts = 0; + saveSettingsLocked(); + sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, + DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + } + + public void reportFailedPasswordAttempt() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BIND_DEVICE_ADMIN, null); + + synchronized (this) { + long ident = Binder.clearCallingIdentity(); + try { + mFailedPasswordAttempts++; + saveSettingsLocked(); + int max = getMaximumFailedPasswordsForWipe(null); + if (max > 0 && mFailedPasswordAttempts >= max) { + wipeDataLocked(0); + } + sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_FAILED, + DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void reportSuccessfulPasswordAttempt() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BIND_DEVICE_ADMIN, null); + + synchronized (this) { + if (mFailedPasswordAttempts != 0 || mPasswordOwner >= 0) { + long ident = Binder.clearCallingIdentity(); + try { + mFailedPasswordAttempts = 0; + mPasswordOwner = -1; + saveSettingsLocked(); + sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED, + DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + + pw.println("Permission Denial: can't dump DevicePolicyManagerService from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + final Printer p = new PrintWriterPrinter(pw); + + synchronized (this) { + p.println("Current Device Policy Manager state:"); + + p.println(" Enabled Device Admins:"); + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin ap = mAdminList.get(i); + if (ap != null) { + pw.print(" "); pw.print(ap.info.getComponent().flattenToShortString()); + pw.println(":"); + ap.dump(" ", pw); + } + } + + pw.println(" "); + pw.print(" mActivePasswordQuality=0x"); + pw.println(Integer.toHexString(mActivePasswordQuality)); + pw.print(" mActivePasswordLength="); pw.println(mActivePasswordLength); + pw.print(" mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts); + pw.print(" mPasswordOwner="); pw.println(mPasswordOwner); + } + } +} diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java index 57af029..62cf707 100644 --- a/services/java/com/android/server/DeviceStorageMonitorService.java +++ b/services/java/com/android/server/DeviceStorageMonitorService.java @@ -34,25 +34,29 @@ import android.os.ServiceManager; import android.os.StatFs; import android.os.SystemClock; import android.os.SystemProperties; -import android.provider.Settings.Gservices; +import android.provider.Settings; import android.util.Config; import android.util.EventLog; -import android.util.Log; +import android.util.Slog; import android.provider.Settings; /** - * This class implements a service to monitor the amount of disk storage space - * on the device. If the free storage on device is less than a tunable threshold value - * (default is 10%. this value is a gservices parameter) a low memory notification is - * displayed to alert the user. If the user clicks on the low memory notification the - * Application Manager application gets launched to let the user free storage space. - * Event log events: - * A low memory event with the free storage on device in bytes is logged to the event log - * when the device goes low on storage space. - * The amount of free storage on the device is periodically logged to the event log. The log - * interval is a gservices parameter with a default value of 12 hours - * When the free storage differential goes below a threshold(again a gservices parameter with - * a default value of 2MB), the free memory is logged to the event log + * This class implements a service to monitor the amount of disk + * storage space on the device. If the free storage on device is less + * than a tunable threshold value (a secure settings parameter; + * default 10%) a low memory notification is displayed to alert the + * user. If the user clicks on the low memory notification the + * Application Manager application gets launched to let the user free + * storage space. + * + * Event log events: A low memory event with the free storage on + * device in bytes is logged to the event log when the device goes low + * on storage space. The amount of free storage on the device is + * periodically logged to the event log. The log interval is a secure + * settings parameter with a default value of 12 hours. When the free + * storage differential goes below a threshold (again a secure + * settings parameter with a default value of 2MB), the free memory is + * logged to the event log. */ class DeviceStorageMonitorService extends Binder { private static final String TAG = "DeviceStorageMonitorService"; @@ -63,9 +67,6 @@ class DeviceStorageMonitorService extends Binder { private static final int LOW_MEMORY_NOTIFICATION_ID = 1; private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; private static final int DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes - private static final int EVENT_LOG_STORAGE_BELOW_THRESHOLD = 2744; - 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; // on /data @@ -104,7 +105,7 @@ class DeviceStorageMonitorService extends Binder { public void handleMessage(Message msg) { //dont handle an invalid message if (msg.what != DEVICE_MEMORY_WHAT) { - Log.e(TAG, "Will not process invalid message"); + Slog.e(TAG, "Will not process invalid message"); return; } checkMemory(msg.arg1 == _TRUE); @@ -115,7 +116,7 @@ class DeviceStorageMonitorService extends Binder { public void onRemoveCompleted(String packageName, boolean succeeded) { mClearSucceeded = succeeded; mClearingCache = false; - if(localLOGV) Log.i(TAG, " Clear succeeded:"+mClearSucceeded + if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded +", mClearingCache:"+mClearingCache+" Forcing memory check"); postCheckMemoryMsg(false, 0); } @@ -134,9 +135,9 @@ class DeviceStorageMonitorService extends Binder { if (!"".equals(debugFreeMem)) { mFreeMem = Long.parseLong(debugFreeMem); } - // Read the log interval from Gservices - long freeMemLogInterval = Gservices.getLong(mContentResolver, - Gservices.SYS_FREE_STORAGE_LOG_INTERVAL, + // Read the log interval from secure settings + long freeMemLogInterval = Settings.Secure.getLong(mContentResolver, + Settings.Secure.SYS_FREE_STORAGE_LOG_INTERVAL, DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000; //log the amount of free memory in event log long currTime = SystemClock.elapsedRealtime(); @@ -159,18 +160,18 @@ class DeviceStorageMonitorService extends Binder { // ignore; report -1 } mCacheFileStats.restat(CACHE_PATH); - EventLog.writeEvent(EVENT_LOG_FREE_STORAGE_LEFT, + EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT, mFreeMem, mFreeSystem, mFreeCache); } - // Read the reporting threshold from Gservices - long threshold = Gservices.getLong(mContentResolver, - Gservices.DISK_FREE_CHANGE_REPORTING_THRESHOLD, + // Read the reporting threshold from secure settings + long threshold = Settings.Secure.getLong(mContentResolver, + Settings.Secure.DISK_FREE_CHANGE_REPORTING_THRESHOLD, DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD); // If mFree changed significantly log the new value long delta = mFreeMem - mLastReportedFreeMem; if (delta > threshold || delta < -threshold) { mLastReportedFreeMem = mFreeMem; - EventLog.writeEvent(EVENT_LOG_STORAGE_BELOW_THRESHOLD, mFreeMem); + EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem); } } @@ -181,11 +182,11 @@ class DeviceStorageMonitorService extends Binder { } mClearingCache = true; try { - if (localLOGV) Log.i(TAG, "Clearing cache"); + if (localLOGV) Slog.i(TAG, "Clearing cache"); IPackageManager.Stub.asInterface(ServiceManager.getService("package")). freeStorageAndNotify(getMemThreshold(), mClearCacheObserver); } catch (RemoteException e) { - Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e); + Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e); mClearingCache = false; mClearSucceeded = false; } @@ -197,15 +198,15 @@ class DeviceStorageMonitorService extends Binder { // and should be accessed via a lock but even if it does this test will fail now and //hopefully the next time this flag will be set to the correct value. if(mClearingCache) { - if(localLOGV) Log.i(TAG, "Thread already running just skip"); + if(localLOGV) Slog.i(TAG, "Thread already running just skip"); //make sure the thread is not hung for too long long diffTime = System.currentTimeMillis() - mThreadStartTime; if(diffTime > (10*60*1000)) { - Log.w(TAG, "Thread that clears cache file seems to run for ever"); + Slog.w(TAG, "Thread that clears cache file seems to run for ever"); } } else { restatDataDir(); - if (localLOGV) Log.v(TAG, "freeMemory="+mFreeMem); + if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem); //post intent to NotificationManager to display icon if necessary long memThreshold = getMemThreshold(); @@ -219,23 +220,23 @@ class DeviceStorageMonitorService extends Binder { mClearSucceeded = false; clearCache(); } else { - Log.i(TAG, "Running low on memory. Sending notification"); + Slog.i(TAG, "Running low on memory. Sending notification"); sendNotification(); mLowMemFlag = true; } } else { - if (localLOGV) Log.v(TAG, "Running low on memory " + + if (localLOGV) Slog.v(TAG, "Running low on memory " + "notification already sent. do nothing"); } } else { if (mLowMemFlag) { - Log.i(TAG, "Memory available. Cancelling notification"); + Slog.i(TAG, "Memory available. Cancelling notification"); cancelNotification(); mLowMemFlag = false; } } } - if(localLOGV) Log.i(TAG, "Posting Message again"); + if(localLOGV) Slog.i(TAG, "Posting Message again"); //keep posting messages to itself periodically postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL); } @@ -250,15 +251,15 @@ class DeviceStorageMonitorService extends Binder { /* * just query settings to retrieve the memory threshold. - * Preferred this over using a ContentObserver since Settings.Gservices caches the value + * Preferred this over using a ContentObserver since Settings.Secure caches the value * any way */ private long getMemThreshold() { - int value = Settings.Gservices.getInt( + int value = Settings.Secure.getInt( mContentResolver, - Settings.Gservices.SYS_STORAGE_THRESHOLD_PERCENTAGE, + Settings.Secure.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); - if(localLOGV) Log.v(TAG, "Threshold Percentage="+value); + if(localLOGV) Slog.v(TAG, "Threshold Percentage="+value); //evaluate threshold value return mTotalMemory*value; } @@ -290,9 +291,9 @@ class DeviceStorageMonitorService extends Binder { * application */ private final void sendNotification() { - if(localLOGV) Log.i(TAG, "Sending low memory notification"); + if(localLOGV) Slog.i(TAG, "Sending low memory notification"); //log the event to event log with the amount of free storage(in bytes) left on the device - EventLog.writeEvent(EVENT_LOG_LOW_STORAGE_NOTIFICATION, mFreeMem); + EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem); // Pack up the values and broadcast them to everyone Intent lowMemIntent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE); lowMemIntent.putExtra("memory", mFreeMem); @@ -318,7 +319,7 @@ class DeviceStorageMonitorService extends Binder { * Cancels low storage notification and sends OK intent. */ private final void cancelNotification() { - if(localLOGV) Log.i(TAG, "Canceling low memory notification"); + if(localLOGV) Slog.i(TAG, "Canceling low memory notification"); NotificationManager mNotificationMgr = (NotificationManager)mContext.getSystemService( Context.NOTIFICATION_SERVICE); diff --git a/services/java/com/android/server/DiskStatsService.java b/services/java/com/android/server/DiskStatsService.java new file mode 100644 index 0000000..8ef974a --- /dev/null +++ b/services/java/com/android/server/DiskStatsService.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.Context; +import android.os.Binder; +import android.os.Environment; +import android.os.FileUtils; +import android.os.StatFs; +import android.os.SystemClock; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * This service exists only as a "dumpsys" target which reports + * statistics about the status of the disk. + */ +public class DiskStatsService extends Binder { + private final Context mContext; + + public DiskStatsService(Context context) { + mContext = context; + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + // This data is accessible to any app -- no permission check needed. + + // Run a quick-and-dirty performance test: write 512 bytes + byte[] junk = new byte[512]; + for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes + + File tmp = new File(Environment.getDataDirectory(), "system/perftest.tmp"); + FileOutputStream fos = null; + IOException error = null; + + long before = SystemClock.uptimeMillis(); + try { + fos = new FileOutputStream(tmp); + fos.write(junk); + } catch (IOException e) { + error = e; + } finally { + try { if (fos != null) fos.close(); } catch (IOException e) {} + } + + long after = SystemClock.uptimeMillis(); + if (tmp.exists()) tmp.delete(); + + if (error != null) { + pw.print("Test-Error: "); + pw.println(error.toString()); + } else { + pw.print("Latency: "); + pw.print(after - before); + pw.println("ms [512B Data Write]"); + } + + reportFreeSpace(Environment.getDataDirectory(), "Data", pw); + reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw); + reportFreeSpace(new File("/system"), "System", pw); + + // TODO: Read /proc/yaffs and report interesting values; + // add configurable (through args) performance test parameters. + } + + private void reportFreeSpace(File path, String name, PrintWriter pw) { + try { + StatFs statfs = new StatFs(path.getPath()); + long bsize = statfs.getBlockSize(); + long avail = statfs.getAvailableBlocks(); + long total = statfs.getBlockCount(); + if (bsize <= 0 || total <= 0) { + throw new IllegalArgumentException( + "Invalid stat: bsize=" + bsize + " avail=" + avail + " total=" + total); + } + + pw.print(name); + pw.print("-Free: "); + pw.print(avail * bsize / 1024); + pw.print("K / "); + pw.print(total * bsize / 1024); + pw.print("K total = "); + pw.print(avail * 100 / total); + pw.println("% free"); + } catch (IllegalArgumentException e) { + pw.print(name); + pw.print("-Error: "); + pw.println(e.toString()); + return; + } + } +} diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index 2fff54c0..bee8872 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -16,14 +16,15 @@ package com.android.server; -import android.app.Activity; -import android.app.KeyguardManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.content.ActivityNotFoundException; -import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.media.AudioManager; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; import android.os.Handler; import android.os.Message; import android.os.SystemClock; @@ -31,8 +32,7 @@ import android.os.UEventObserver; import android.provider.Settings; import android.server.BluetoothService; import android.util.Log; - -import com.android.internal.widget.LockPatternUtils; +import android.util.Slog; import java.io.FileNotFoundException; import java.io.FileReader; @@ -47,81 +47,43 @@ class DockObserver extends UEventObserver { private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock"; private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state"; + private static final int MSG_DOCK_STATE = 0; + private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + private boolean mSystemReady; private final Context mContext; private PowerManagerService mPowerManager; - private KeyguardManager.KeyguardLock mKeyguardLock; - private boolean mKeyguardDisabled; - private LockPatternUtils mLockPatternUtils; - - // The broadcast receiver which receives the result of the ordered broadcast sent when - // the dock state changes. The original ordered broadcast is sent with an initial result - // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g., - // to RESULT_CANCELED, then the intent to start a dock app will not be sent. - private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (getResultCode() != Activity.RESULT_OK) { - return; - } - - // Launch a dock activity - String category; - switch (mDockState) { - case Intent.EXTRA_DOCK_STATE_CAR: - category = Intent.CATEGORY_CAR_DOCK; - break; - case Intent.EXTRA_DOCK_STATE_DESK: - category = Intent.CATEGORY_DESK_DOCK; - break; - default: - category = null; - break; - } - if (category != null) { - intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(category); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - try { - mContext.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(TAG, e.getCause()); - } - } - } - }; - public DockObserver(Context context, PowerManagerService pm) { mContext = context; mPowerManager = pm; - mLockPatternUtils = new LockPatternUtils(context.getContentResolver()); init(); // set initial status + startObserving(DOCK_UEVENT_MATCH); } @Override public void onUEvent(UEventObserver.UEvent event) { if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Dock UEVENT: " + event.toString()); + Slog.v(TAG, "Dock UEVENT: " + event.toString()); } synchronized (this) { try { int newState = Integer.parseInt(event.get("SWITCH_STATE")); if (newState != mDockState) { - int oldState = mDockState; + mPreviousDockState = mDockState; mDockState = newState; if (mSystemReady) { // Don't force screen on when undocking from the desk dock. // The change in power state will do this anyway. // FIXME - we should be configurable. - if (oldState != Intent.EXTRA_DOCK_STATE_DESK || - newState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { + if (mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK || + mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(), false, true); } @@ -129,7 +91,7 @@ class DockObserver extends UEventObserver { } } } catch (NumberFormatException e) { - Log.e(TAG, "Could not parse switch state from event " + event); + Slog.e(TAG, "Could not parse switch state from event " + event); } } } @@ -140,21 +102,17 @@ class DockObserver extends UEventObserver { try { FileReader file = new FileReader(DOCK_STATE_PATH); int len = file.read(buffer, 0, 1024); - mDockState = Integer.valueOf((new String(buffer, 0, len)).trim()); + mPreviousDockState = mDockState = Integer.valueOf((new String(buffer, 0, len)).trim()); } catch (FileNotFoundException e) { - Log.w(TAG, "This kernel does not have dock station support"); + Slog.w(TAG, "This kernel does not have dock station support"); } catch (Exception e) { - Log.e(TAG, "" , e); + Slog.e(TAG, "" , e); } } void systemReady() { synchronized (this) { - KeyguardManager keyguardManager = - (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE); - mKeyguardLock = keyguardManager.newKeyguardLock(TAG); - // don't bother broadcasting undocked here if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { update(); @@ -164,37 +122,73 @@ class DockObserver extends UEventObserver { } private final void update() { - mHandler.sendEmptyMessage(0); + mHandler.sendEmptyMessage(MSG_DOCK_STATE); } private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { - synchronized (this) { - Log.i(TAG, "Dock state changed: " + mDockState); - if (Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { - Log.i(TAG, "Device not provisioned, skipping dock broadcast"); - return; - } - // Pack up the values and broadcast them to everyone - Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); - intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); - - // Check if this is Bluetooth Dock - String address = BluetoothService.readDockBluetoothAddress(); - if (address != null) - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, - BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address)); - - // Send the ordered broadcast; the result receiver will receive after all - // broadcasts have been sent. If any broadcast receiver changes the result - // code from the initial value of RESULT_OK, then the result receiver will - // not launch the corresponding dock application. This gives apps a chance - // to override the behavior and stay in their app even when the device is - // placed into a dock. - mContext.sendStickyOrderedBroadcast( - intent, mResultReceiver, null, Activity.RESULT_OK, null, null); + switch (msg.what) { + case MSG_DOCK_STATE: + synchronized (this) { + Slog.i(TAG, "Dock state changed: " + mDockState); + + final ContentResolver cr = mContext.getContentResolver(); + + if (Settings.Secure.getInt(cr, + Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { + Slog.i(TAG, "Device not provisioned, skipping dock broadcast"); + return; + } + // Pack up the values and broadcast them to everyone + Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); + + // Check if this is Bluetooth Dock + String address = BluetoothService.readDockBluetoothAddress(); + if (address != null) + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, + BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address)); + + // User feedback to confirm dock connection. Particularly + // useful for flaky contact pins... + if (Settings.System.getInt(cr, + Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1) + { + String whichSound = null; + if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { + if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) { + whichSound = Settings.System.DESK_UNDOCK_SOUND; + } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) { + whichSound = Settings.System.CAR_UNDOCK_SOUND; + } + } else { + if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { + whichSound = Settings.System.DESK_DOCK_SOUND; + } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) { + whichSound = Settings.System.CAR_DOCK_SOUND; + } + } + + if (whichSound != null) { + final String soundPath = Settings.System.getString(cr, whichSound); + if (soundPath != null) { + final Uri soundUri = Uri.parse("file://" + soundPath); + if (soundUri != null) { + final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); + if (sfx != null) { + sfx.setStreamType(AudioManager.STREAM_SYSTEM); + sfx.play(); + } + } + } + } + } + + mContext.sendStickyBroadcast(intent); + } + break; } } }; diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java new file mode 100644 index 0000000..0de11c6 --- /dev/null +++ b/services/java/com/android/server/DropBoxManagerService.java @@ -0,0 +1,727 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Debug; +import android.os.DropBoxManager; +import android.os.Handler; +import android.os.ParcelFileDescriptor; +import android.os.StatFs; +import android.os.SystemClock; +import android.provider.Settings; +import android.text.format.Time; +import android.util.Slog; + +import com.android.internal.os.IDropBoxManagerService; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.zip.GZIPOutputStream; + +/** + * Implementation of {@link IDropBoxManagerService} using the filesystem. + * Clients use {@link DropBoxManager} to access this service. + */ +public final class DropBoxManagerService extends IDropBoxManagerService.Stub { + private static final String TAG = "DropBoxManagerService"; + private static final int DEFAULT_AGE_SECONDS = 3 * 86400; + private static final int DEFAULT_MAX_FILES = 1000; + private static final int DEFAULT_QUOTA_KB = 5 * 1024; + private static final int DEFAULT_QUOTA_PERCENT = 10; + private static final int DEFAULT_RESERVE_PERCENT = 10; + private static final int QUOTA_RESCAN_MILLIS = 5000; + + private static final boolean PROFILE_DUMP = false; + + // TODO: This implementation currently uses one file per entry, which is + // inefficient for smallish entries -- consider using a single queue file + // per tag (or even globally) instead. + + // The cached context and derived objects + + private final Context mContext; + private final ContentResolver mContentResolver; + private final File mDropBoxDir; + + // Accounting of all currently written log files (set in init()). + + private FileList mAllFiles = null; + private HashMap<String, FileList> mFilesByTag = null; + + // Various bits of disk information + + private StatFs mStatFs = null; + private int mBlockSize = 0; + private int mCachedQuotaBlocks = 0; // Space we can use: computed from free space, etc. + private long mCachedQuotaUptimeMillis = 0; + + // Ensure that all log entries have a unique timestamp + private long mLastTimestamp = 0; + + /** Receives events that might indicate a need to clean up files. */ + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mCachedQuotaUptimeMillis = 0; // Force a re-check of quota size + + // Run the initialization in the background (not this main thread). + // The init() and trimToFit() methods are synchronized, so they still + // block other users -- but at least the onReceive() call can finish. + new Thread() { + public void run() { + try { + init(); + trimToFit(); + } catch (IOException e) { + Slog.e(TAG, "Can't init", e); + } + } + }.start(); + } + }; + + /** + * Creates an instance of managed drop box storage. Normally there is one of these + * run by the system, but others can be created for testing and other purposes. + * + * @param context to use for receiving free space & gservices intents + * @param path to store drop box entries in + */ + public DropBoxManagerService(final Context context, File path) { + mDropBoxDir = path; + + // Set up intent receivers + mContext = context; + mContentResolver = context.getContentResolver(); + context.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW)); + + mContentResolver.registerContentObserver( + Settings.Secure.CONTENT_URI, true, + new ContentObserver(new Handler()) { + public void onChange(boolean selfChange) { + mReceiver.onReceive(context, (Intent) null); + } + }); + + // The real work gets done lazily in init() -- that way service creation always + // succeeds, and things like disk problems cause individual method failures. + } + + /** Unregisters broadcast receivers and any other hooks -- for test instances */ + public void stop() { + mContext.unregisterReceiver(mReceiver); + } + + public void add(DropBoxManager.Entry entry) { + File temp = null; + OutputStream output = null; + final String tag = entry.getTag(); + try { + int flags = entry.getFlags(); + if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException(); + + init(); + if (!isTagEnabled(tag)) return; + long max = trimToFit(); + long lastTrim = System.currentTimeMillis(); + + byte[] buffer = new byte[mBlockSize]; + InputStream input = entry.getInputStream(); + + // First, accumulate up to one block worth of data in memory before + // deciding whether to compress the data or not. + + int read = 0; + while (read < buffer.length) { + int n = input.read(buffer, read, buffer.length - read); + if (n <= 0) break; + read += n; + } + + // If we have at least one block, compress it -- otherwise, just write + // the data in uncompressed form. + + temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp"); + output = new FileOutputStream(temp); + if (read == buffer.length && ((flags & DropBoxManager.IS_GZIPPED) == 0)) { + output = new GZIPOutputStream(output); + flags = flags | DropBoxManager.IS_GZIPPED; + } + + do { + output.write(buffer, 0, read); + + long now = System.currentTimeMillis(); + if (now - lastTrim > 30 * 1000) { + max = trimToFit(); // In case data dribbles in slowly + lastTrim = now; + } + + read = input.read(buffer); + if (read <= 0) { + output.close(); // Get a final size measurement + output = null; + } else { + output.flush(); // So the size measurement is pseudo-reasonable + } + + long len = temp.length(); + if (len > max) { + Slog.w(TAG, "Dropping: " + tag + " (" + temp.length() + " > " + max + " bytes)"); + temp.delete(); + temp = null; // Pass temp = null to createEntry() to leave a tombstone + break; + } + } while (read > 0); + + createEntry(temp, tag, flags); + temp = null; + } catch (IOException e) { + Slog.e(TAG, "Can't write: " + tag, e); + } finally { + try { if (output != null) output.close(); } catch (IOException e) {} + entry.close(); + if (temp != null) temp.delete(); + } + } + + public boolean isTagEnabled(String tag) { + return !"disabled".equals(Settings.Secure.getString( + mContentResolver, Settings.Secure.DROPBOX_TAG_PREFIX + tag)); + } + + public synchronized DropBoxManager.Entry getNextEntry(String tag, long millis) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.READ_LOGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("READ_LOGS permission required"); + } + + try { + init(); + } catch (IOException e) { + Slog.e(TAG, "Can't init", e); + return null; + } + + FileList list = tag == null ? mAllFiles : mFilesByTag.get(tag); + if (list == null) return null; + + for (EntryFile entry : list.contents.tailSet(new EntryFile(millis + 1))) { + if (entry.tag == null) continue; + if ((entry.flags & DropBoxManager.IS_EMPTY) != 0) { + return new DropBoxManager.Entry(entry.tag, entry.timestampMillis); + } + try { + return new DropBoxManager.Entry( + entry.tag, entry.timestampMillis, entry.file, entry.flags); + } catch (IOException e) { + Slog.e(TAG, "Can't read: " + entry.file, e); + // Continue to next file + } + } + + return null; + } + + public synchronized 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 DropBoxManagerService"); + return; + } + + try { + init(); + } catch (IOException e) { + pw.println("Can't initialize: " + e); + Slog.e(TAG, "Can't init", e); + return; + } + + if (PROFILE_DUMP) Debug.startMethodTracing("/data/trace/dropbox.dump"); + + StringBuilder out = new StringBuilder(); + boolean doPrint = false, doFile = false; + ArrayList<String> searchArgs = new ArrayList<String>(); + for (int i = 0; args != null && i < args.length; i++) { + if (args[i].equals("-p") || args[i].equals("--print")) { + doPrint = true; + } else if (args[i].equals("-f") || args[i].equals("--file")) { + doFile = true; + } else if (args[i].startsWith("-")) { + out.append("Unknown argument: ").append(args[i]).append("\n"); + } else { + searchArgs.add(args[i]); + } + } + + out.append("Drop box contents: ").append(mAllFiles.contents.size()).append(" entries\n"); + + if (!searchArgs.isEmpty()) { + out.append("Searching for:"); + for (String a : searchArgs) out.append(" ").append(a); + out.append("\n"); + } + + int numFound = 0, numArgs = searchArgs.size(); + Time time = new Time(); + out.append("\n"); + for (EntryFile entry : mAllFiles.contents) { + time.set(entry.timestampMillis); + String date = time.format("%Y-%m-%d %H:%M:%S"); + boolean match = true; + for (int i = 0; i < numArgs && match; i++) { + String arg = searchArgs.get(i); + match = (date.contains(arg) || arg.equals(entry.tag)); + } + if (!match) continue; + + numFound++; + if (doPrint) out.append("========================================\n"); + out.append(date).append(" ").append(entry.tag == null ? "(no tag)" : entry.tag); + if (entry.file == null) { + out.append(" (no file)\n"); + continue; + } else if ((entry.flags & DropBoxManager.IS_EMPTY) != 0) { + out.append(" (contents lost)\n"); + continue; + } else { + out.append(" ("); + if ((entry.flags & DropBoxManager.IS_GZIPPED) != 0) out.append("compressed "); + out.append((entry.flags & DropBoxManager.IS_TEXT) != 0 ? "text" : "data"); + out.append(", ").append(entry.file.length()).append(" bytes)\n"); + } + + if (doFile || (doPrint && (entry.flags & DropBoxManager.IS_TEXT) == 0)) { + if (!doPrint) out.append(" "); + out.append(entry.file.getPath()).append("\n"); + } + + if ((entry.flags & DropBoxManager.IS_TEXT) != 0 && (doPrint || !doFile)) { + DropBoxManager.Entry dbe = null; + try { + dbe = new DropBoxManager.Entry( + entry.tag, entry.timestampMillis, entry.file, entry.flags); + + if (doPrint) { + InputStreamReader r = new InputStreamReader(dbe.getInputStream()); + char[] buf = new char[4096]; + boolean newline = false; + for (;;) { + int n = r.read(buf); + if (n <= 0) break; + out.append(buf, 0, n); + newline = (buf[n - 1] == '\n'); + + // Flush periodically when printing to avoid out-of-memory. + if (out.length() > 65536) { + pw.write(out.toString()); + out.setLength(0); + } + } + if (!newline) out.append("\n"); + } else { + String text = dbe.getText(70); + boolean truncated = (text.length() == 70); + out.append(" ").append(text.trim().replace('\n', '/')); + if (truncated) out.append(" ..."); + out.append("\n"); + } + } catch (IOException e) { + out.append("*** ").append(e.toString()).append("\n"); + Slog.e(TAG, "Can't read: " + entry.file, e); + } finally { + if (dbe != null) dbe.close(); + } + } + + if (doPrint) out.append("\n"); + } + + if (numFound == 0) out.append("(No entries found.)\n"); + + if (args == null || args.length == 0) { + if (!doPrint) out.append("\n"); + out.append("Usage: dumpsys dropbox [--print|--file] [YYYY-mm-dd] [HH:MM:SS] [tag]\n"); + } + + pw.write(out.toString()); + if (PROFILE_DUMP) Debug.stopMethodTracing(); + } + + /////////////////////////////////////////////////////////////////////////// + + /** Chronologically sorted list of {@link #EntryFile} */ + private static final class FileList implements Comparable<FileList> { + public int blocks = 0; + public final TreeSet<EntryFile> contents = new TreeSet<EntryFile>(); + + /** Sorts bigger FileList instances before smaller ones. */ + public final int compareTo(FileList o) { + if (blocks != o.blocks) return o.blocks - blocks; + if (this == o) return 0; + if (hashCode() < o.hashCode()) return -1; + if (hashCode() > o.hashCode()) return 1; + return 0; + } + } + + /** Metadata describing an on-disk log file. */ + private static final class EntryFile implements Comparable<EntryFile> { + public final String tag; + public final long timestampMillis; + public final int flags; + public final File file; + public final int blocks; + + /** Sorts earlier EntryFile instances before later ones. */ + public final int compareTo(EntryFile o) { + if (timestampMillis < o.timestampMillis) return -1; + if (timestampMillis > o.timestampMillis) return 1; + if (file != null && o.file != null) return file.compareTo(o.file); + if (o.file != null) return -1; + if (file != null) return 1; + if (this == o) return 0; + if (hashCode() < o.hashCode()) return -1; + if (hashCode() > o.hashCode()) return 1; + return 0; + } + + /** + * Moves an existing temporary file to a new log filename. + * @param temp file to rename + * @param dir to store file in + * @param tag to use for new log file name + * @param timestampMillis of log entry + * @param flags for the entry data + * @param blockSize to use for space accounting + * @throws IOException if the file can't be moved + */ + public EntryFile(File temp, File dir, String tag,long timestampMillis, + int flags, int blockSize) throws IOException { + if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException(); + + this.tag = tag; + this.timestampMillis = timestampMillis; + this.flags = flags; + this.file = new File(dir, Uri.encode(tag) + "@" + timestampMillis + + ((flags & DropBoxManager.IS_TEXT) != 0 ? ".txt" : ".dat") + + ((flags & DropBoxManager.IS_GZIPPED) != 0 ? ".gz" : "")); + + if (!temp.renameTo(this.file)) { + throw new IOException("Can't rename " + temp + " to " + this.file); + } + this.blocks = (int) ((this.file.length() + blockSize - 1) / blockSize); + } + + /** + * Creates a zero-length tombstone for a file whose contents were lost. + * @param dir to store file in + * @param tag to use for new log file name + * @param timestampMillis of log entry + * @throws IOException if the file can't be created. + */ + public EntryFile(File dir, String tag, long timestampMillis) throws IOException { + this.tag = tag; + this.timestampMillis = timestampMillis; + this.flags = DropBoxManager.IS_EMPTY; + this.file = new File(dir, Uri.encode(tag) + "@" + timestampMillis + ".lost"); + this.blocks = 0; + new FileOutputStream(this.file).close(); + } + + /** + * Extracts metadata from an existing on-disk log filename. + * @param file name of existing log file + * @param blockSize to use for space accounting + */ + public EntryFile(File file, int blockSize) { + this.file = file; + this.blocks = (int) ((this.file.length() + blockSize - 1) / blockSize); + + String name = file.getName(); + int at = name.lastIndexOf('@'); + if (at < 0) { + this.tag = null; + this.timestampMillis = 0; + this.flags = DropBoxManager.IS_EMPTY; + return; + } + + int flags = 0; + this.tag = Uri.decode(name.substring(0, at)); + if (name.endsWith(".gz")) { + flags |= DropBoxManager.IS_GZIPPED; + name = name.substring(0, name.length() - 3); + } + if (name.endsWith(".lost")) { + flags |= DropBoxManager.IS_EMPTY; + name = name.substring(at + 1, name.length() - 5); + } else if (name.endsWith(".txt")) { + flags |= DropBoxManager.IS_TEXT; + name = name.substring(at + 1, name.length() - 4); + } else if (name.endsWith(".dat")) { + name = name.substring(at + 1, name.length() - 4); + } else { + this.flags = DropBoxManager.IS_EMPTY; + this.timestampMillis = 0; + return; + } + this.flags = flags; + + long millis; + try { millis = Long.valueOf(name); } catch (NumberFormatException e) { millis = 0; } + this.timestampMillis = millis; + } + + /** + * Creates a EntryFile object with only a timestamp for comparison purposes. + * @param timestampMillis to compare with. + */ + public EntryFile(long millis) { + this.tag = null; + this.timestampMillis = millis; + this.flags = DropBoxManager.IS_EMPTY; + this.file = null; + this.blocks = 0; + } + } + + /////////////////////////////////////////////////////////////////////////// + + /** If never run before, scans disk contents to build in-memory tracking data. */ + private synchronized void init() throws IOException { + if (mStatFs == null) { + if (!mDropBoxDir.isDirectory() && !mDropBoxDir.mkdirs()) { + throw new IOException("Can't mkdir: " + mDropBoxDir); + } + try { + mStatFs = new StatFs(mDropBoxDir.getPath()); + mBlockSize = mStatFs.getBlockSize(); + } catch (IllegalArgumentException e) { // StatFs throws this on error + throw new IOException("Can't statfs: " + mDropBoxDir); + } + } + + if (mAllFiles == null) { + File[] files = mDropBoxDir.listFiles(); + if (files == null) throw new IOException("Can't list files: " + mDropBoxDir); + + mAllFiles = new FileList(); + mFilesByTag = new HashMap<String, FileList>(); + + // Scan pre-existing files. + for (File file : files) { + if (file.getName().endsWith(".tmp")) { + Slog.i(TAG, "Cleaning temp file: " + file); + file.delete(); + continue; + } + + EntryFile entry = new EntryFile(file, mBlockSize); + if (entry.tag == null) { + Slog.w(TAG, "Unrecognized file: " + file); + continue; + } else if (entry.timestampMillis == 0) { + Slog.w(TAG, "Invalid filename: " + file); + file.delete(); + continue; + } + + enrollEntry(entry); + } + } + } + + /** Adds a disk log file to in-memory tracking for accounting and enumeration. */ + private synchronized void enrollEntry(EntryFile entry) { + mAllFiles.contents.add(entry); + mAllFiles.blocks += entry.blocks; + + // mFilesByTag is used for trimming, so don't list empty files. + // (Zero-length/lost files are trimmed by date from mAllFiles.) + + if (entry.tag != null && entry.file != null && entry.blocks > 0) { + FileList tagFiles = mFilesByTag.get(entry.tag); + if (tagFiles == null) { + tagFiles = new FileList(); + mFilesByTag.put(entry.tag, tagFiles); + } + tagFiles.contents.add(entry); + tagFiles.blocks += entry.blocks; + } + } + + /** Moves a temporary file to a final log filename and enrolls it. */ + private synchronized void createEntry(File temp, String tag, int flags) throws IOException { + long t = System.currentTimeMillis(); + + // Require each entry to have a unique timestamp; if there are entries + // >10sec in the future (due to clock skew), drag them back to avoid + // keeping them around forever. + + SortedSet<EntryFile> tail = mAllFiles.contents.tailSet(new EntryFile(t + 10000)); + EntryFile[] future = null; + if (!tail.isEmpty()) { + future = tail.toArray(new EntryFile[tail.size()]); + tail.clear(); // Remove from mAllFiles + } + + if (!mAllFiles.contents.isEmpty()) { + t = Math.max(t, mAllFiles.contents.last().timestampMillis + 1); + } + + if (future != null) { + for (EntryFile late : future) { + mAllFiles.blocks -= late.blocks; + FileList tagFiles = mFilesByTag.get(late.tag); + if (tagFiles != null && tagFiles.contents.remove(late)) { + tagFiles.blocks -= late.blocks; + } + if ((late.flags & DropBoxManager.IS_EMPTY) == 0) { + enrollEntry(new EntryFile( + late.file, mDropBoxDir, late.tag, t++, late.flags, mBlockSize)); + } else { + enrollEntry(new EntryFile(mDropBoxDir, late.tag, t++)); + } + } + } + + if (temp == null) { + enrollEntry(new EntryFile(mDropBoxDir, tag, t)); + } else { + enrollEntry(new EntryFile(temp, mDropBoxDir, tag, t, flags, mBlockSize)); + } + } + + /** + * Trims the files on disk to make sure they aren't using too much space. + * @return the overall quota for storage (in bytes) + */ + private synchronized long trimToFit() { + // Expunge aged items (including tombstones marking deleted data). + + int ageSeconds = Settings.Secure.getInt(mContentResolver, + Settings.Secure.DROPBOX_AGE_SECONDS, DEFAULT_AGE_SECONDS); + int maxFiles = Settings.Secure.getInt(mContentResolver, + Settings.Secure.DROPBOX_MAX_FILES, DEFAULT_MAX_FILES); + long cutoffMillis = System.currentTimeMillis() - ageSeconds * 1000; + while (!mAllFiles.contents.isEmpty()) { + EntryFile entry = mAllFiles.contents.first(); + if (entry.timestampMillis > cutoffMillis && mAllFiles.contents.size() < maxFiles) break; + + FileList tag = mFilesByTag.get(entry.tag); + if (tag != null && tag.contents.remove(entry)) tag.blocks -= entry.blocks; + if (mAllFiles.contents.remove(entry)) mAllFiles.blocks -= entry.blocks; + if (entry.file != null) entry.file.delete(); + } + + // Compute overall quota (a fraction of available free space) in blocks. + // The quota changes dynamically based on the amount of free space; + // that way when lots of data is available we can use it, but we'll get + // out of the way if storage starts getting tight. + + long uptimeMillis = SystemClock.uptimeMillis(); + if (uptimeMillis > mCachedQuotaUptimeMillis + QUOTA_RESCAN_MILLIS) { + int quotaPercent = Settings.Secure.getInt(mContentResolver, + Settings.Secure.DROPBOX_QUOTA_PERCENT, DEFAULT_QUOTA_PERCENT); + int reservePercent = Settings.Secure.getInt(mContentResolver, + Settings.Secure.DROPBOX_RESERVE_PERCENT, DEFAULT_RESERVE_PERCENT); + int quotaKb = Settings.Secure.getInt(mContentResolver, + Settings.Secure.DROPBOX_QUOTA_KB, DEFAULT_QUOTA_KB); + + mStatFs.restat(mDropBoxDir.getPath()); + int available = mStatFs.getAvailableBlocks(); + int nonreserved = available - mStatFs.getBlockCount() * reservePercent / 100; + int maximum = quotaKb * 1024 / mBlockSize; + mCachedQuotaBlocks = Math.min(maximum, Math.max(0, nonreserved * quotaPercent / 100)); + mCachedQuotaUptimeMillis = uptimeMillis; + } + + // If we're using too much space, delete old items to make room. + // + // We trim each tag independently (this is why we keep per-tag lists). + // Space is "fairly" shared between tags -- they are all squeezed + // equally until enough space is reclaimed. + // + // A single circular buffer (a la logcat) would be simpler, but this + // way we can handle fat/bursty data (like 1MB+ bugreports, 300KB+ + // kernel crash dumps, and 100KB+ ANR reports) without swamping small, + // well-behaved data streams (event statistics, profile data, etc). + // + // Deleted files are replaced with zero-length tombstones to mark what + // was lost. Tombstones are expunged by age (see above). + + if (mAllFiles.blocks > mCachedQuotaBlocks) { + Slog.i(TAG, "Usage (" + mAllFiles.blocks + ") > Quota (" + mCachedQuotaBlocks + ")"); + + // Find a fair share amount of space to limit each tag + int unsqueezed = mAllFiles.blocks, squeezed = 0; + TreeSet<FileList> tags = new TreeSet<FileList>(mFilesByTag.values()); + for (FileList tag : tags) { + if (squeezed > 0 && tag.blocks <= (mCachedQuotaBlocks - unsqueezed) / squeezed) { + break; + } + unsqueezed -= tag.blocks; + squeezed++; + } + int tagQuota = (mCachedQuotaBlocks - unsqueezed) / squeezed; + + // Remove old items from each tag until it meets the per-tag quota. + for (FileList tag : tags) { + if (mAllFiles.blocks < mCachedQuotaBlocks) break; + while (tag.blocks > tagQuota && !tag.contents.isEmpty()) { + EntryFile entry = tag.contents.first(); + if (tag.contents.remove(entry)) tag.blocks -= entry.blocks; + if (mAllFiles.contents.remove(entry)) mAllFiles.blocks -= entry.blocks; + + try { + if (entry.file != null) entry.file.delete(); + enrollEntry(new EntryFile(mDropBoxDir, entry.tag, entry.timestampMillis)); + } catch (IOException e) { + Slog.e(TAG, "Can't write tombstone file", e); + } + } + } + } + + return mCachedQuotaBlocks * mBlockSize; + } +} diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java index 28f09f5..81ae26f 100644 --- a/services/java/com/android/server/EntropyService.java +++ b/services/java/com/android/server/EntropyService.java @@ -27,7 +27,7 @@ import android.os.Environment; import android.os.Handler; import android.os.Message; import android.os.SystemProperties; -import android.util.Log; +import android.util.Slog; /** * A service designed to load and periodically save "randomness" @@ -48,14 +48,15 @@ import android.util.Log; * instead of periodically. */ public class EntropyService extends Binder { - private static final String ENTROPY_FILENAME = getSystemDir() + "/entropy.dat"; private static final String TAG = "EntropyService"; private static final int ENTROPY_WHAT = 1; private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs - private static final String RANDOM_DEV = "/dev/urandom"; private static final long START_TIME = System.currentTimeMillis(); private static final long START_NANOTIME = System.nanoTime(); + private final String randomDevice; + private final String entropyFile; + /** * Handler that periodically updates the entropy on disk. */ @@ -63,7 +64,7 @@ public class EntropyService extends Binder { @Override public void handleMessage(Message msg) { if (msg.what != ENTROPY_WHAT) { - Log.e(TAG, "Will not process invalid message"); + Slog.e(TAG, "Will not process invalid message"); return; } writeEntropy(); @@ -72,6 +73,16 @@ public class EntropyService extends Binder { }; public EntropyService() { + this(getSystemDir() + "/entropy.dat", "/dev/urandom"); + } + + /** Test only interface, not for public use */ + public EntropyService(String entropyFile, String randomDevice) { + if (randomDevice == null) { throw new NullPointerException("randomDevice"); } + if (entropyFile == null) { throw new NullPointerException("entropyFile"); } + + this.randomDevice = randomDevice; + this.entropyFile = entropyFile; loadInitialEntropy(); addDeviceSpecificEntropy(); writeEntropy(); @@ -85,17 +96,17 @@ public class EntropyService extends Binder { private void loadInitialEntropy() { try { - RandomBlock.fromFile(ENTROPY_FILENAME).toFile(RANDOM_DEV); + RandomBlock.fromFile(entropyFile).toFile(randomDevice); } catch (IOException e) { - Log.w(TAG, "unable to load initial entropy (first boot?)", e); + Slog.w(TAG, "unable to load initial entropy (first boot?)", e); } } private void writeEntropy() { try { - RandomBlock.fromFile(RANDOM_DEV).toFile(ENTROPY_FILENAME); + RandomBlock.fromFile(randomDevice).toFile(entropyFile); } catch (IOException e) { - Log.w(TAG, "unable to write entropy", e); + Slog.w(TAG, "unable to write entropy", e); } } @@ -116,7 +127,7 @@ public class EntropyService extends Binder { private void addDeviceSpecificEntropy() { PrintWriter out = null; try { - out = new PrintWriter(new FileOutputStream(RANDOM_DEV)); + out = new PrintWriter(new FileOutputStream(randomDevice)); out.println("Copyright (C) 2009 The Android Open Source Project"); out.println("All Your Randomness Are Belong To Us"); out.println(START_TIME); @@ -131,7 +142,7 @@ public class EntropyService extends Binder { out.println(System.currentTimeMillis()); out.println(System.nanoTime()); } catch (IOException e) { - Log.w(TAG, "Unable to add device specific data to the entropy pool", e); + Slog.w(TAG, "Unable to add device specific data to the entropy pool", e); } finally { if (out != null) { out.close(); diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags new file mode 100644 index 0000000..5429c0c --- /dev/null +++ b/services/java/com/android/server/EventLogTags.logtags @@ -0,0 +1,139 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +option java_package com.android.server + +# --------------------------- +# BatteryService.java +# --------------------------- +2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1) +2723 battery_status (status|1|5),(health|1|5),(present|1|5),(plugged|1|5),(technology|3) +# This is logged when battery goes from discharging to charging. +# It lets us count the total amount of time between charges and the discharge level +2730 battery_discharge (duration|2|3),(minLevel|1|6),(maxLevel|1|6) + + +# --------------------------- +# PowerManagerService.java +# --------------------------- +# This is logged when the device is being forced to sleep (typically by +# the user pressing the power button). +2724 power_sleep_requested (wakeLocksCleared|1|1) +# This is logged when the screen on broadcast has completed +2725 power_screen_broadcast_send (wakelockCount|1|1) +# This is logged when the screen broadcast has completed +2726 power_screen_broadcast_done (on|1|5),(broadcastDuration|2|3),(wakelockCount|1|1) +# This is logged when the screen on broadcast has completed +2727 power_screen_broadcast_stop (which|1|5),(wakelockCount|1|1) +# This is logged when the screen is turned on or off. +2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1) +# This is logged when the partial wake lock (keeping the device awake +# regardless of whether the screen is off) is acquired or released. +2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3) + +# +# Leave IDs through 2739 for more power logs (2730 used by battery_discharge above) +# + + +# --------------------------- +# DeviceStorageMonitoryService.java +# --------------------------- +# The disk space free on the /data partition, in bytes +2744 free_storage_changed (data|2|2) +# Device low memory notification and disk space free on the /data partition, in bytes at that time +2745 low_storage (data|2|2) +# disk space free on the /data, /system, and /cache partitions in bytes +2746 free_storage_left (data|2|2),(system|2|2),(cache|2|2) + + +# --------------------------- +# NotificationManagerService.java +# --------------------------- +# when a NotificationManager.notify is called +2750 notification_enqueue (pkg|3),(id|1|5),(notification|3) +# when someone tries to cancel a notification, the notification manager sometimes +# calls this with flags too +2751 notification_cancel (pkg|3),(id|1|5),(required_flags|1) +# when someone tries to cancel all of the notifications for a particular package +2752 notification_cancel_all (pkg|3),(required_flags|1) + + +# --------------------------- +# Watchdog.java +# --------------------------- +2802 watchdog (Service|3) +2803 watchdog_proc_pss (Process|3),(Pid|1|5),(Pss|1|2) +2804 watchdog_soft_reset (Process|3),(Pid|1|5),(MaxPss|1|2),(Pss|1|2),(Skip|3) +2805 watchdog_hard_reset (Process|3),(Pid|1|5),(MaxPss|1|2),(Pss|1|2) +2806 watchdog_pss_stats (EmptyPss|1|2),(EmptyCount|1|1),(BackgroundPss|1|2),(BackgroundCount|1|1),(ServicePss|1|2),(ServiceCount|1|1),(VisiblePss|1|2),(VisibleCount|1|1),(ForegroundPss|1|2),(ForegroundCount|1|1),(NoPssCount|1|1) +2807 watchdog_proc_stats (DeathsInOne|1|1),(DeathsInTwo|1|1),(DeathsInThree|1|1),(DeathsInFour|1|1),(DeathsInFive|1|1) +2808 watchdog_scheduled_reboot (Now|2|1),(Interval|1|3),(StartTime|1|3),(Window|1|3),(Skip|3) +2809 watchdog_meminfo (MemFree|1|2),(Buffers|1|2),(Cached|1|2),(Active|1|2),(Inactive|1|2),(AnonPages|1|2),(Mapped|1|2),(Slab|1|2),(SReclaimable|1|2),(SUnreclaim|1|2),(PageTables|1|2) +2810 watchdog_vmstat (runtime|2|3),(pgfree|1|1),(pgactivate|1|1),(pgdeactivate|1|1),(pgfault|1|1),(pgmajfault|1|1) +2811 watchdog_requested_reboot (NoWait|1|1),(ScheduleInterval|1|3),(RecheckInterval|1|3),(StartTime|1|3),(Window|1|3),(MinScreenOff|1|3),(MinNextAlarm|1|3) + + +# --------------------------- +# BackupManagerService.java +# --------------------------- +2820 backup_data_changed (Package|3) +2821 backup_start (Transport|3) +2822 backup_transport_failure (Package|3) +2823 backup_agent_failure (Package|3),(Message|3) +2824 backup_package (Package|3),(Size|1|2) +2825 backup_success (Packages|1|1),(Time|1|3) +2826 backup_reset (Transport|3) +2827 backup_initialize +2830 restore_start (Transport|3),(Source|2|5) +2831 restore_transport_failure +2832 restore_agent_failure (Package|3),(Message|3) +2833 restore_package (Package|3),(Size|1|2) +2834 restore_success (Packages|1|1),(Time|1|3) + + +# --------------------------- +# SystemServer.java +# --------------------------- +# SystemServer.run() starts: +3010 boot_progress_system_run (time|2|3) + + +# --------------------------- +# PackageManagerService.java +# --------------------------- +# Package Manager starts: +3060 boot_progress_pms_start (time|2|3) +# Package Manager .apk scan starts: +3070 boot_progress_pms_system_scan_start (time|2|3) +# Package Manager .apk scan starts: +3080 boot_progress_pms_data_scan_start (time|2|3) +# Package Manager .apk scan ends: +3090 boot_progress_pms_scan_end (time|2|3) +# Package Manager ready: +3100 boot_progress_pms_ready (time|2|3) +# + check activity_launch_time for Home app + + +# --------------------------- +# WindowManagerService.java +# --------------------------- +# Out of memory for surfaces. +31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3) + + +# --------------------------- +# InputMethodManagerService.java +# --------------------------- +# Re-connecting to input method service because we haven't received its interface +32000 imf_force_reconnect_ime (IME|4),(Time Since Connect|2|3),(Showing|1|1) + + +# --------------------------- +# ConnectivityService.java +# --------------------------- +# Connectivity state changed: +# [31-13] Reserved for future use +# [12- 9] Network subtype (for mobile network, as defined by TelephonyManager) +# [ 8- 3] Detailed state ordinal (as defined by NetworkInfo.DetailedState) +# [ 2- 0] Network type (as defined by ConnectivityManager) +50020 connectivity_state_changed (custom|1|5) diff --git a/services/java/com/android/server/FallbackCheckinService.java b/services/java/com/android/server/FallbackCheckinService.java deleted file mode 100644 index cf22446..0000000 --- a/services/java/com/android/server/FallbackCheckinService.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.content.Context; -import android.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/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java index 9d69564..6f0a91d 100644 --- a/services/java/com/android/server/HeadsetObserver.java +++ b/services/java/com/android/server/HeadsetObserver.java @@ -24,7 +24,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.UEventObserver; -import android.util.Log; +import android.util.Slog; import android.media.AudioManager; import java.io.FileReader; @@ -66,12 +66,12 @@ class HeadsetObserver extends UEventObserver { @Override public void onUEvent(UEventObserver.UEvent event) { - if (LOG) Log.v(TAG, "Headset UEVENT: " + event.toString()); + if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString()); try { update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE"))); } catch (NumberFormatException e) { - Log.e(TAG, "Could not parse switch state from event " + event); + Slog.e(TAG, "Could not parse switch state from event " + event); } } @@ -91,9 +91,9 @@ class HeadsetObserver extends UEventObserver { newName = new String(buffer, 0, len).trim(); } catch (FileNotFoundException e) { - Log.w(TAG, "This kernel does not have wired headset support"); + Slog.w(TAG, "This kernel does not have wired headset support"); } catch (Exception e) { - Log.e(TAG, "" , e); + Slog.e(TAG, "" , e); } update(newName, newState); @@ -167,7 +167,7 @@ class HeadsetObserver extends UEventObserver { intent.putExtra("name", headsetName); intent.putExtra("microphone", microphone); - if (LOG) Log.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone); + if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone); // TODO: Should we require a permission? ActivityManagerNative.broadcastStickyIntent(intent, null); } diff --git a/services/java/com/android/server/INativeDaemonConnectorCallbacks.java b/services/java/com/android/server/INativeDaemonConnectorCallbacks.java new file mode 100644 index 0000000..6fbf713 --- /dev/null +++ b/services/java/com/android/server/INativeDaemonConnectorCallbacks.java @@ -0,0 +1,24 @@ + +/* + * 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; + +interface INativeDaemonConnectorCallbacks { + + void onDaemonConnected(); + boolean onEvent(int code, String raw, String[] cooked); +} diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java index 6f207e0..07a74da 100644 --- a/services/java/com/android/server/InputDevice.java +++ b/services/java/com/android/server/InputDevice.java @@ -16,12 +16,14 @@ package com.android.server; -import android.util.Log; +import android.util.Slog; import android.view.Display; import android.view.MotionEvent; import android.view.Surface; import android.view.WindowManagerPolicy; +import java.io.PrintWriter; + public class InputDevice { static final boolean DEBUG_POINTERS = false; static final boolean DEBUG_HACKS = false; @@ -32,6 +34,16 @@ public class InputDevice { /** Maximum number of pointers we will track and report. */ static final int MAX_POINTERS = 10; + /** + * Slop distance for jumpy pointer detection. + * The vertical range of the screen divided by this is our epsilon value. + */ + private static final int JUMPY_EPSILON_DIVISOR = 212; + + /** Number of jumpy points to drop for touchscreens that need it. */ + private static final int JUMPY_TRANSITION_DROPS = 3; + private static final int JUMPY_DROP_LIMIT = 3; + final int id; final int classes; final String name; @@ -58,6 +70,7 @@ public class InputDevice { float yMoveScale; MotionEvent currentMove = null; boolean changed = false; + boolean everChanged = false; long mDownTime = 0; // The currently assigned pointer IDs, corresponding to the last data. @@ -81,6 +94,9 @@ public class InputDevice { // Used to determine whether we dropped bad data, to avoid doing // it repeatedly. final boolean[] mDroppedBadPoint = new boolean[MAX_POINTERS]; + + // Used to count the number of jumpy points dropped. + private int mJumpyPointsDropped = 0; // Used to perform averaging of reported coordinates, to smooth // the data and filter out transients during a release. @@ -103,6 +119,56 @@ public class InputDevice { int mAddingPointerOffset = 0; final boolean[] mDown = new boolean[MAX_POINTERS]; + void dumpIntArray(PrintWriter pw, int[] array) { + pw.print("["); + for (int i=0; i<array.length; i++) { + if (i > 0) pw.print(", "); + pw.print(array[i]); + } + pw.print("]"); + } + + void dumpBooleanArray(PrintWriter pw, boolean[] array) { + pw.print("["); + for (int i=0; i<array.length; i++) { + if (i > 0) pw.print(", "); + pw.print(array[i] ? "true" : "false"); + } + pw.print("]"); + } + + void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("xPrecision="); pw.print(xPrecision); + pw.print(" yPrecision="); pw.println(yPrecision); + pw.print(prefix); pw.print("xMoveScale="); pw.print(xMoveScale); + pw.print(" yMoveScale="); pw.println(yMoveScale); + if (currentMove != null) { + pw.print(prefix); pw.print("currentMove="); pw.println(currentMove); + } + if (changed || mDownTime != 0) { + pw.print(prefix); pw.print("changed="); pw.print(changed); + pw.print(" mDownTime="); pw.println(mDownTime); + } + pw.print(prefix); pw.print("mPointerIds="); dumpIntArray(pw, mPointerIds); + pw.println(""); + if (mSkipLastPointers || mLastNumPointers != 0) { + pw.print(prefix); pw.print("mSkipLastPointers="); pw.print(mSkipLastPointers); + pw.print(" mLastNumPointers="); pw.println(mLastNumPointers); + pw.print(prefix); pw.print("mLastData="); dumpIntArray(pw, mLastData); + pw.println(""); + } + if (mNextNumPointers != 0) { + pw.print(prefix); pw.print("mNextNumPointers="); pw.println(mNextNumPointers); + pw.print(prefix); pw.print("mNextData="); dumpIntArray(pw, mNextData); + pw.println(""); + } + pw.print(prefix); pw.print("mDroppedBadPoint="); + dumpBooleanArray(pw, mDroppedBadPoint); pw.println(""); + pw.print(prefix); pw.print("mAddingPointerOffset="); pw.println(mAddingPointerOffset); + pw.print(prefix); pw.print("mDown="); + dumpBooleanArray(pw, mDown); pw.println(""); + } + MotionState(int mx, int my) { xPrecision = mx; yPrecision = my; @@ -142,7 +208,7 @@ public class InputDevice { final int ioff = i * MotionEvent.NUM_SAMPLE_DATA; //final int x = mNextData[ioff + MotionEvent.SAMPLE_X]; final int y = mNextData[ioff + MotionEvent.SAMPLE_Y]; - if (DEBUG_HACKS) Log.v("InputDevice", "Looking at next point #" + i + ": y=" + y); + if (DEBUG_HACKS) Slog.v("InputDevice", "Looking at next point #" + i + ": y=" + y); boolean dropped = false; if (!mDroppedBadPoint[i] && mLastNumPointers > 0) { dropped = true; @@ -156,7 +222,7 @@ public class InputDevice { int dy = y - mLastData[joff + MotionEvent.SAMPLE_Y]; //if (dx < 0) dx = -dx; if (dy < 0) dy = -dy; - if (DEBUG_HACKS) Log.v("InputDevice", "Comparing with last point #" + j + if (DEBUG_HACKS) Slog.v("InputDevice", "Comparing with last point #" + j + ": y=" + mLastData[joff] + " dy=" + dy); if (dy < maxDy) { dropped = false; @@ -168,7 +234,7 @@ public class InputDevice { } if (dropped) { dropped = true; - Log.i("InputDevice", "Dropping bad point #" + i + Slog.i("InputDevice", "Dropping bad point #" + i + ": newY=" + y + " closestDy=" + closestDy + " maxDy=" + maxDy); mNextData[ioff + MotionEvent.SAMPLE_Y] = closestY; @@ -179,6 +245,164 @@ public class InputDevice { } } + void dropJumpyPoint(InputDevice dev) { + // We should always have absY, but let's be paranoid. + if (dev.absY == null) { + return; + } + final int jumpyEpsilon = dev.absY.range / JUMPY_EPSILON_DIVISOR; + + final int nextNumPointers = mNextNumPointers; + final int lastNumPointers = mLastNumPointers; + final int[] nextData = mNextData; + final int[] lastData = mLastData; + + if (nextNumPointers != mLastNumPointers) { + if (DEBUG_HACKS) { + Slog.d("InputDevice", "Different pointer count " + lastNumPointers + + " -> " + nextNumPointers); + for (int i = 0; i < nextNumPointers; i++) { + int ioff = i * MotionEvent.NUM_SAMPLE_DATA; + Slog.d("InputDevice", "Pointer " + i + " (" + + mNextData[ioff + MotionEvent.SAMPLE_X] + ", " + + mNextData[ioff + MotionEvent.SAMPLE_Y] + ")"); + } + } + + // Just drop the first few events going from 1 to 2 pointers. + // They're bad often enough that they're not worth considering. + if (lastNumPointers == 1 && nextNumPointers == 2 + && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) { + mNextNumPointers = 1; + mJumpyPointsDropped++; + } else if (lastNumPointers == 2 && nextNumPointers == 1 + && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) { + // The event when we go from 2 -> 1 tends to be messed up too + System.arraycopy(lastData, 0, nextData, 0, + lastNumPointers * MotionEvent.NUM_SAMPLE_DATA); + mNextNumPointers = lastNumPointers; + mJumpyPointsDropped++; + + if (DEBUG_HACKS) { + for (int i = 0; i < mNextNumPointers; i++) { + int ioff = i * MotionEvent.NUM_SAMPLE_DATA; + Slog.d("InputDevice", "Pointer " + i + " replaced (" + + mNextData[ioff + MotionEvent.SAMPLE_X] + ", " + + mNextData[ioff + MotionEvent.SAMPLE_Y] + ")"); + } + } + } else { + mJumpyPointsDropped = 0; + + if (DEBUG_HACKS) { + Slog.d("InputDevice", "Transition - drop limit reset"); + } + } + return; + } + + // A 'jumpy' point is one where the coordinate value for one axis + // has jumped to the other pointer's location. No need to do anything + // else if we only have one pointer. + if (nextNumPointers < 2) { + return; + } + + int badPointerIndex = -1; + int badPointerReplaceXWith = 0; + int badPointerReplaceYWith = 0; + int badPointerDistance = Integer.MIN_VALUE; + for (int i = nextNumPointers - 1; i >= 0; i--) { + boolean dropx = false; + boolean dropy = false; + + // Limit how many times a jumpy point can get dropped. + if (mJumpyPointsDropped < JUMPY_DROP_LIMIT) { + final int ioff = i * MotionEvent.NUM_SAMPLE_DATA; + final int x = nextData[ioff + MotionEvent.SAMPLE_X]; + final int y = nextData[ioff + MotionEvent.SAMPLE_Y]; + + if (DEBUG_HACKS) { + Slog.d("InputDevice", "Point " + i + " (" + x + ", " + y + ")"); + } + + // Check if a touch point is too close to another's coordinates + for (int j = 0; j < nextNumPointers && !dropx && !dropy; j++) { + if (j == i) { + continue; + } + + final int joff = j * MotionEvent.NUM_SAMPLE_DATA; + final int xOther = nextData[joff + MotionEvent.SAMPLE_X]; + final int yOther = nextData[joff + MotionEvent.SAMPLE_Y]; + + dropx = Math.abs(x - xOther) <= jumpyEpsilon; + dropy = Math.abs(y - yOther) <= jumpyEpsilon; + } + + if (dropx) { + int xreplace = lastData[MotionEvent.SAMPLE_X]; + int yreplace = lastData[MotionEvent.SAMPLE_Y]; + int distance = Math.abs(yreplace - y); + for (int j = 1; j < lastNumPointers; j++) { + final int joff = j * MotionEvent.NUM_SAMPLE_DATA; + int lasty = lastData[joff + MotionEvent.SAMPLE_Y]; + int currDist = Math.abs(lasty - y); + if (currDist < distance) { + xreplace = lastData[joff + MotionEvent.SAMPLE_X]; + yreplace = lasty; + distance = currDist; + } + } + + int badXDelta = Math.abs(xreplace - x); + if (badXDelta > badPointerDistance) { + badPointerDistance = badXDelta; + badPointerIndex = i; + badPointerReplaceXWith = xreplace; + badPointerReplaceYWith = yreplace; + } + } else if (dropy) { + int xreplace = lastData[MotionEvent.SAMPLE_X]; + int yreplace = lastData[MotionEvent.SAMPLE_Y]; + int distance = Math.abs(xreplace - x); + for (int j = 1; j < lastNumPointers; j++) { + final int joff = j * MotionEvent.NUM_SAMPLE_DATA; + int lastx = lastData[joff + MotionEvent.SAMPLE_X]; + int currDist = Math.abs(lastx - x); + if (currDist < distance) { + xreplace = lastx; + yreplace = lastData[joff + MotionEvent.SAMPLE_Y]; + distance = currDist; + } + } + + int badYDelta = Math.abs(yreplace - y); + if (badYDelta > badPointerDistance) { + badPointerDistance = badYDelta; + badPointerIndex = i; + badPointerReplaceXWith = xreplace; + badPointerReplaceYWith = yreplace; + } + } + } + } + if (badPointerIndex >= 0) { + if (DEBUG_HACKS) { + Slog.d("InputDevice", "Replacing bad pointer " + badPointerIndex + + " with (" + badPointerReplaceXWith + ", " + badPointerReplaceYWith + + ")"); + } + + final int offset = badPointerIndex * MotionEvent.NUM_SAMPLE_DATA; + nextData[offset + MotionEvent.SAMPLE_X] = badPointerReplaceXWith; + nextData[offset + MotionEvent.SAMPLE_Y] = badPointerReplaceYWith; + mJumpyPointsDropped++; + } else { + mJumpyPointsDropped = 0; + } + } + /** * Special hack for devices that have bad screen data: aggregate and * compute averages of the coordinate data, to reduce the amount of @@ -188,7 +412,7 @@ public class InputDevice { int nextNumPointers) { final int numPointers = mLastNumPointers; final int[] rawData = mLastData; - if (DEBUG_HACKS) Log.v("InputDevice", "lastNumPointers=" + lastNumPointers + if (DEBUG_HACKS) Slog.v("InputDevice", "lastNumPointers=" + lastNumPointers + " nextNumPointers=" + nextNumPointers + " numPointers=" + numPointers); for (int i=0; i<numPointers; i++) { @@ -202,7 +426,7 @@ public class InputDevice { if (lastNumPointers < nextNumPointers) { // This pointer is going down. Clear its history // and start fresh. - if (DEBUG_HACKS) Log.v("InputDevice", "Pointer down @ index " + if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer down @ index " + upOrDownPointer + " id " + mPointerIds[i]); mHistoryDataStart[i] = 0; mHistoryDataEnd[i] = 0; @@ -215,7 +439,7 @@ public class InputDevice { // The pointer is going up. Just fall through to // recompute the last averaged point (and don't add // it as a new point to include in the average). - if (DEBUG_HACKS) Log.v("InputDevice", "Pointer up @ index " + if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer up @ index " + upOrDownPointer + " id " + mPointerIds[i]); } } else { @@ -228,7 +452,7 @@ public class InputDevice { int dx = newX-oldX; int dy = newY-oldY; int delta = dx*dx + dy*dy; - if (DEBUG_HACKS) Log.v("InputDevice", "Delta from last: " + delta); + if (DEBUG_HACKS) Slog.v("InputDevice", "Delta from last: " + delta); if (delta >= (75*75)) { // Magic number, if moving farther than this, turn // off filtering to avoid lag in response. @@ -284,7 +508,7 @@ public class InputDevice { totalPressure += pressure; x /= totalPressure; y /= totalPressure; - if (DEBUG_HACKS) Log.v("InputDevice", "Averaging " + totalPressure + if (DEBUG_HACKS) Slog.v("InputDevice", "Averaging " + totalPressure + " weight: (" + x + "," + y + ")"); mAveragedData[ioff + MotionEvent.SAMPLE_X] = x; mAveragedData[ioff + MotionEvent.SAMPLE_Y] = y; @@ -305,7 +529,7 @@ public class InputDevice { final int[] nextData = mNextData; final int id = nextIndex * MotionEvent.NUM_SAMPLE_DATA; - if (DEBUG_POINTERS) Log.v("InputDevice", "assignPointer: nextIndex=" + if (DEBUG_POINTERS) Slog.v("InputDevice", "assignPointer: nextIndex=" + nextIndex + " dataOff=" + id); final int x1 = nextData[id + MotionEvent.SAMPLE_X]; final int y1 = nextData[id + MotionEvent.SAMPLE_Y]; @@ -329,7 +553,7 @@ public class InputDevice { } } - if (DEBUG_POINTERS) Log.v("InputDevice", "New index " + nextIndex + if (DEBUG_POINTERS) Slog.v("InputDevice", "New index " + nextIndex + " best old index=" + bestIndex + " (distance=" + bestDistance + ")"); next2Last[nextIndex] = bestIndex; @@ -344,7 +568,7 @@ public class InputDevice { return false; } - if (DEBUG_POINTERS) Log.v("InputDevice", "Old index " + bestIndex + if (DEBUG_POINTERS) Slog.v("InputDevice", "Old index " + bestIndex + " has multiple best new pointers!"); last2Next[bestIndex] = -2; @@ -369,7 +593,7 @@ public class InputDevice { last2Next[i] = -1; } - if (DEBUG_POINTERS) Log.v("InputDevice", + if (DEBUG_POINTERS) Slog.v("InputDevice", "Update pointers: lastNumPointers=" + lastNumPointers + " nextNumPointers=" + nextNumPointers); @@ -385,7 +609,7 @@ public class InputDevice { // new pointer locations find their best previous location is // the same. if (conflicts) { - if (DEBUG_POINTERS) Log.v("InputDevice", "Resolving conflicts"); + if (DEBUG_POINTERS) Slog.v("InputDevice", "Resolving conflicts"); for (int i=0; i<lastNumPointers; i++) { if (last2Next[i] != -2) { @@ -396,7 +620,7 @@ public class InputDevice { // we should do something like the one described at // http://portal.acm.org/citation.cfm?id=997856 - if (DEBUG_POINTERS) Log.v("InputDevice", + if (DEBUG_POINTERS) Slog.v("InputDevice", "Resolving last index #" + i); int numFound; @@ -416,7 +640,7 @@ public class InputDevice { } if (worstJ >= 0) { - if (DEBUG_POINTERS) Log.v("InputDevice", + if (DEBUG_POINTERS) Slog.v("InputDevice", "Worst new pointer: " + worstJ + " (distance=" + worstDistance + ")"); if (assignPointer(worstJ, false)) { @@ -434,13 +658,13 @@ public class InputDevice { if (lastNumPointers < nextNumPointers) { // We have one or more new pointers that are down. Create a // new pointer identifier for one of them. - if (DEBUG_POINTERS) Log.v("InputDevice", "Adding new pointer"); + if (DEBUG_POINTERS) Slog.v("InputDevice", "Adding new pointer"); int nextId = 0; int i=0; while (i < lastNumPointers) { if (mPointerIds[i] > nextId) { // Found a hole, insert the pointer here. - if (DEBUG_POINTERS) Log.v("InputDevice", + if (DEBUG_POINTERS) Slog.v("InputDevice", "Inserting new pointer at hole " + i); System.arraycopy(mPointerIds, i, mPointerIds, i+1, lastNumPointers-i); @@ -453,7 +677,7 @@ public class InputDevice { nextId++; } - if (DEBUG_POINTERS) Log.v("InputDevice", + if (DEBUG_POINTERS) Slog.v("InputDevice", "New pointer id " + nextId + " at index " + i); mLastNumPointers++; @@ -463,7 +687,7 @@ public class InputDevice { // And assign this identifier to the first new pointer. for (int j=0; j<nextNumPointers; j++) { if (next2Last[j] < 0) { - if (DEBUG_POINTERS) Log.v("InputDevice", + if (DEBUG_POINTERS) Slog.v("InputDevice", "Assigning new id to new pointer index " + j); next2Last[j] = i; break; @@ -477,7 +701,7 @@ public class InputDevice { for (int i=0; i<nextNumPointers; i++) { int lastIndex = next2Last[i]; if (lastIndex >= 0) { - if (DEBUG_POINTERS) Log.v("InputDevice", + if (DEBUG_POINTERS) Slog.v("InputDevice", "Copying next pointer index " + i + " to last index " + lastIndex); System.arraycopy(nextData, i*MotionEvent.NUM_SAMPLE_DATA, @@ -489,10 +713,10 @@ public class InputDevice { if (lastNumPointers > nextNumPointers) { // One or more pointers has gone up. Find the first one, // and adjust accordingly. - if (DEBUG_POINTERS) Log.v("InputDevice", "Removing old pointer"); + if (DEBUG_POINTERS) Slog.v("InputDevice", "Removing old pointer"); for (int i=0; i<lastNumPointers; i++) { if (last2Next[i] == -1) { - if (DEBUG_POINTERS) Log.v("InputDevice", + if (DEBUG_POINTERS) Slog.v("InputDevice", "Removing old pointer at index " + i); retIndex = i; break; @@ -531,7 +755,7 @@ public class InputDevice { final int lastNumPointers = mLastNumPointers; final int nextNumPointers = mNextNumPointers; if (mNextNumPointers > MAX_POINTERS) { - Log.w("InputDevice", "Number of pointers " + mNextNumPointers + Slog.w("InputDevice", "Number of pointers " + mNextNumPointers + " exceeded maximum of " + MAX_POINTERS); mNextNumPointers = MAX_POINTERS; } @@ -549,7 +773,7 @@ public class InputDevice { final int numPointers = mLastNumPointers; - if (DEBUG_POINTERS) Log.v("InputDevice", "Processing " + if (DEBUG_POINTERS) Slog.v("InputDevice", "Processing " + numPointers + " pointers (going from " + lastNumPointers + " to " + nextNumPointers + ")"); @@ -570,14 +794,14 @@ public class InputDevice { mDownTime = curTime; } else { action = MotionEvent.ACTION_POINTER_DOWN - | (upOrDownPointer << MotionEvent.ACTION_POINTER_ID_SHIFT); + | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT); } } else { if (numPointers == 1) { action = MotionEvent.ACTION_UP; } else { action = MotionEvent.ACTION_POINTER_UP - | (upOrDownPointer << MotionEvent.ACTION_POINTER_ID_SHIFT); + | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT); } } currentMove = null; @@ -661,13 +885,13 @@ public class InputDevice { } if (currentMove != null) { - if (false) Log.i("InputDevice", "Adding batch x=" + if (false) Slog.i("InputDevice", "Adding batch x=" + reportData[MotionEvent.SAMPLE_X] + " y=" + reportData[MotionEvent.SAMPLE_Y] + " to " + currentMove); currentMove.addBatch(curTime, reportData, metaState); if (WindowManagerPolicy.WATCH_POINTER) { - Log.i("KeyInputQueue", "Updating: " + currentMove); + Slog.i("KeyInputQueue", "Updating: " + currentMove); } return null; } @@ -748,13 +972,13 @@ public class InputDevice { } if (currentMove != null) { - if (false) Log.i("InputDevice", "Adding batch x=" + if (false) Slog.i("InputDevice", "Adding batch x=" + scaled[MotionEvent.SAMPLE_X] + " y=" + scaled[MotionEvent.SAMPLE_Y] + " to " + currentMove); currentMove.addBatch(curTime, scaled, metaState); if (WindowManagerPolicy.WATCH_POINTER) { - Log.i("KeyInputQueue", "Updating: " + currentMove); + Slog.i("KeyInputQueue", "Updating: " + currentMove); } return null; } @@ -775,6 +999,14 @@ public class InputDevice { int range; int flat; int fuzz; + + final void dump(PrintWriter pw) { + pw.print("minValue="); pw.print(minValue); + pw.print(" maxValue="); pw.print(maxValue); + pw.print(" range="); pw.print(range); + pw.print(" flat="); pw.print(flat); + pw.print(" fuzz="); pw.print(fuzz); + } }; InputDevice(int _id, int _classes, String _name, diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index afcba47..5bf66e4 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2006-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 @@ -16,6 +16,7 @@ package com.android.server; +import com.android.internal.content.PackageMonitor; import com.android.internal.os.HandlerCaller; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethod; @@ -45,10 +46,10 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; -import android.net.Uri; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -60,9 +61,10 @@ import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.SystemClock; import android.provider.Settings; +import android.provider.Settings.Secure; import android.text.TextUtils; import android.util.EventLog; -import android.util.Log; +import android.util.Slog; import android.util.PrintWriterPrinter; import android.util.Printer; import android.view.IWindowManager; @@ -89,24 +91,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final String TAG = "InputManagerService"; static final int MSG_SHOW_IM_PICKER = 1; - + static final int MSG_UNBIND_INPUT = 1000; static final int MSG_BIND_INPUT = 1010; static final int MSG_SHOW_SOFT_INPUT = 1020; static final int MSG_HIDE_SOFT_INPUT = 1030; static final int MSG_ATTACH_TOKEN = 1040; static final int MSG_CREATE_SESSION = 1050; - + static final int MSG_START_INPUT = 2000; static final int MSG_RESTART_INPUT = 2010; - + 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; @@ -115,9 +115,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final IconData mInputMethodData; final IWindowManager mIWindowManager; final HandlerCaller mCaller; - + final InputBindResult mNoBinding = new InputBindResult(null, null, -1); - + // All known input methods. mMethodMap also serves as the global // lock for this class. final ArrayList<InputMethodInfo> mMethodList @@ -127,12 +127,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':'); - + class SessionState { final ClientState client; final IInputMethod method; final IInputMethodSession session; - + @Override public String toString() { return "SessionState{uid " + client.uid + " pid " + client.pid @@ -150,17 +150,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub session = _session; } } - + class ClientState { final IInputMethodClient client; final IInputContext inputContext; final int uid; final int pid; final InputBinding binding; - + boolean sessionRequested; SessionState curSession; - + @Override public String toString() { return "ClientState{" + Integer.toHexString( @@ -177,122 +177,122 @@ public class InputMethodManagerService extends IInputMethodManager.Stub binding = new InputBinding(null, inputContext.asBinder(), uid, pid); } } - + final HashMap<IBinder, ClientState> mClients = new HashMap<IBinder, ClientState>(); - + /** * Set once the system is ready to run third party code. */ boolean mSystemReady; - + /** * Id of the currently selected input method. */ String mCurMethodId; - + /** * The current binding sequence number, incremented every time there is * a new bind performed. */ int mCurSeq; - + /** * The client that is currently bound to an input method. */ 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; - + /** * The input method ID of the input method service that we are currently * connected to or in the process of connecting to. */ String mCurId; - + /** * Set to true if our ServiceConnection is currently actively bound to * a service (whether or not we have gotten its IBinder back yet). */ boolean mHaveConnection; - + /** * Set if the client has asked for the input method to be shown. */ boolean mShowRequested; - + /** * Set if we were explicitly told to show the input method. */ 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; - + /** * The Intent used to connect to the current input method. */ Intent mCurIntent; - + /** * The token we have made for the currently active input method, to * identify it in the future. */ IBinder mCurToken; - + /** * If non-null, this is the input method service we are currently connected * to. */ 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; - + /** * Currently enabled session. Only touched by service thread, not * protected by a lock. */ SessionState mEnabledSession; - + /** * True if the screen is on. The value is true initially. */ boolean mScreenOn = true; - + AlertDialog.Builder mDialogBuilder; AlertDialog mSwitchingDialog; InputMethodInfo[] mIms; CharSequence[] mItems; - + class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { super(handler); @@ -300,14 +300,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.DEFAULT_INPUT_METHOD), false, this); } - + @Override public void onChange(boolean selfChange) { synchronized (mMethodMap) { updateFromSettingsLocked(); } } } - + class ScreenOnOffReceiver extends android.content.BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -319,7 +319,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub hideInputMethodMenu(); return; } else { - Log.w(TAG, "Unexpected intent " + intent); + Slog.w(TAG, "Unexpected intent " + intent); } // Inform the current client of the change in active status @@ -328,76 +328,117 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurClient.client.setActive(mScreenOn); } } catch (RemoteException e) { - Log.w(TAG, "Got RemoteException sending 'screen on/off' notification to pid " + Slog.w(TAG, "Got RemoteException sending 'screen on/off' notification to pid " + mCurClient.pid + " uid " + mCurClient.uid); } } } - - class PackageReceiver extends android.content.BroadcastReceiver { + + class MyPackageMonitor extends PackageMonitor { + @Override - public void onReceive(Context context, Intent intent) { + public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { + synchronized (mMethodMap) { + String curInputMethodId = Settings.Secure.getString(mContext + .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + final int N = mMethodList.size(); + if (curInputMethodId != null) { + for (int i=0; i<N; i++) { + InputMethodInfo imi = mMethodList.get(i); + if (imi.getId().equals(curInputMethodId)) { + for (String pkg : packages) { + if (imi.getPackageName().equals(pkg)) { + if (!doit) { + return true; + } + + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, ""); + chooseNewDefaultIMELocked(); + return true; + } + } + } + } + } + } + return false; + } + + @Override + public void onSomePackagesChanged() { synchronized (mMethodMap) { - buildInputMethodListLocked(mMethodList, mMethodMap); - InputMethodInfo curIm = null; - String curInputMethodId = Settings.Secure.getString(context + String curInputMethodId = Settings.Secure.getString(mContext .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); final int N = mMethodList.size(); if (curInputMethodId != null) { for (int i=0; i<N; i++) { - if (mMethodList.get(i).getId().equals(curInputMethodId)) { - curIm = mMethodList.get(i); + InputMethodInfo imi = mMethodList.get(i); + if (imi.getId().equals(curInputMethodId)) { + curIm = imi; + } + int change = isPackageDisappearing(imi.getPackageName()); + if (change == PACKAGE_TEMPORARY_CHANGE + || change == PACKAGE_PERMANENT_CHANGE) { + Slog.i(TAG, "Input method uninstalled, disabling: " + + imi.getComponent()); + setInputMethodEnabledLocked(imi.getId(), false); } } } - + + buildInputMethodListLocked(mMethodList, mMethodMap); + boolean changed = false; - - Uri uri = intent.getData(); - String pkg = uri != null ? uri.getSchemeSpecificPart() : null; - if (curIm != null && curIm.getPackageName().equals(pkg)) { - ServiceInfo si = null; - try { - si = mContext.getPackageManager().getServiceInfo( - curIm.getComponent(), 0); - } catch (PackageManager.NameNotFoundException ex) { - } - if (si == null) { - // Uh oh, current input method is no longer around! - // Pick another one... - Log.i(TAG, "Current input method removed: " + curInputMethodId); - if (!chooseNewDefaultIME()) { - changed = true; - curIm = null; - curInputMethodId = ""; - Log.i(TAG, "Unsetting current input method"); - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD, - curInputMethodId); + + if (curIm != null) { + int change = isPackageDisappearing(curIm.getPackageName()); + if (change == PACKAGE_TEMPORARY_CHANGE + || change == PACKAGE_PERMANENT_CHANGE) { + ServiceInfo si = null; + try { + si = mContext.getPackageManager().getServiceInfo( + curIm.getComponent(), 0); + } catch (PackageManager.NameNotFoundException ex) { + } + if (si == null) { + // Uh oh, current input method is no longer around! + // Pick another one... + Slog.i(TAG, "Current input method removed: " + curInputMethodId); + if (!chooseNewDefaultIMELocked()) { + changed = true; + curIm = null; + curInputMethodId = ""; + Slog.i(TAG, "Unsetting current input method"); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, + curInputMethodId); + } } } - - } else if (curIm == null) { + } + + if (curIm == null) { // We currently don't have a default input method... is // one now available? - changed = chooseNewDefaultIME(); + changed = chooseNewDefaultIMELocked(); } - + if (changed) { updateFromSettingsLocked(); } } } } - + class MethodCallback extends IInputMethodCallback.Stub { final IInputMethod mMethod; - + MethodCallback(IInputMethod method) { mMethod = method; } - + public void finishedEvent(int seq, boolean handled) throws RemoteException { } @@ -405,7 +446,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub onSessionCreated(mMethod, session); } } - + public InputMethodManagerService(Context context, StatusBarService statusBar) { mContext = context; mHandler = new Handler(this); @@ -416,35 +457,29 @@ public class InputMethodManagerService extends IInputMethodManager.Stub handleMessage(msg); } }); - - IntentFilter packageFilt = new IntentFilter(); - packageFilt.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED); - packageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED); - packageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED); - packageFilt.addDataScheme("package"); - mContext.registerReceiver(new PackageReceiver(), packageFilt); - + + (new MyPackageMonitor()).register(mContext, true); + IntentFilter screenOnOffFilt = new IntentFilter(); screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON); screenOnOffFilt.addAction(Intent.ACTION_SCREEN_OFF); screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt); - + buildInputMethodListLocked(mMethodList, mMethodMap); final String enabledStr = Settings.Secure.getString( mContext.getContentResolver(), Settings.Secure.ENABLED_INPUT_METHODS); - Log.i(TAG, "Enabled input methods: " + enabledStr); + Slog.i(TAG, "Enabled input methods: " + enabledStr); if (enabledStr == null) { - Log.i(TAG, "Enabled input methods has not been set, enabling all"); + Slog.i(TAG, "Enabled input methods has not been set, enabling all"); InputMethodInfo defIm = null; StringBuilder sb = new StringBuilder(256); final int N = mMethodList.size(); for (int i=0; i<N; i++) { InputMethodInfo imi = mMethodList.get(i); - Log.i(TAG, "Adding: " + imi.getId()); + Slog.i(TAG, "Adding: " + imi.getId()); if (i > 0) sb.append(':'); sb.append(imi.getId()); if (defIm == null && imi.getIsDefaultResourceId() != 0) { @@ -453,7 +488,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub imi.getPackageName(), 0).getResources(); if (res.getBoolean(imi.getIsDefaultResourceId())) { defIm = imi; - Log.i(TAG, "Selected default: " + imi.getId()); + Slog.i(TAG, "Selected default: " + imi.getId()); } } catch (PackageManager.NameNotFoundException ex) { } catch (Resources.NotFoundException ex) { @@ -462,7 +497,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (defIm == null && N > 0) { defIm = mMethodList.get(0); - Log.i(TAG, "No default found, using " + defIm.getId()); + Slog.i(TAG, "No default found, using " + defIm.getId()); } Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.ENABLED_INPUT_METHODS, sb.toString()); @@ -471,12 +506,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.DEFAULT_INPUT_METHOD, defIm.getId()); } } - + mStatusBar = statusBar; mInputMethodData = IconData.makeIcon("ime", null, 0, 0, 0); mInputMethodIcon = statusBar.addIcon(mInputMethodData, null); statusBar.setIconVisibility(mInputMethodIcon, false); - + mSettingsObserver = new SettingsObserver(mHandler); updateFromSettingsLocked(); } @@ -490,7 +525,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // The input method manager only throws security exceptions, so let's // log all others. if (!(e instanceof SecurityException)) { - Log.e(TAG, "Input Method Manager Crash", e); + Slog.e(TAG, "Input Method Manager Crash", e); } throw e; } @@ -503,12 +538,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { startInputInnerLocked(); } catch (RuntimeException e) { - Log.w(TAG, "Unexpected exception", e); + Slog.w(TAG, "Unexpected exception", e); } } } } - + public List<InputMethodInfo> getInputMethodList() { synchronized (mMethodMap) { return new ArrayList<InputMethodInfo>(mMethodList); @@ -523,14 +558,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub List<InputMethodInfo> getEnabledInputMethodListLocked() { final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>(); - + final 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()) { InputMethodInfo info = mMethodMap.get(splitter.next()); if (info != null) { @@ -538,7 +573,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } - + return res; } @@ -549,13 +584,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub inputContext, uid, pid)); } } - + public void removeClient(IInputMethodClient client) { synchronized (mMethodMap) { mClients.remove(client.asBinder()); } } - + void executeOrSendMessage(IInterface target, Message msg) { if (target.asBinder() instanceof Binder) { mCaller.sendMessage(msg); @@ -564,10 +599,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub msg.recycle(); } } - + void unbindCurrentClientLocked() { if (mCurClient != null) { - if (DEBUG) Log.v(TAG, "unbindCurrentInputLocked: client = " + if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client = " + mCurClient.client.asBinder()); if (mBoundToMethod) { mBoundToMethod = false; @@ -579,20 +614,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); mCurClient.sessionRequested = false; - + // Call setActive(false) on the old client try { mCurClient.client.setActive(false); } catch (RemoteException e) { - Log.w(TAG, "Got RemoteException sending setActive(false) notification to pid " + Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid " + mCurClient.pid + " uid " + mCurClient.uid); } mCurClient = null; - + hideInputMethodMenuLocked(); } } - + private int getImeShowFlags() { int flags = 0; if (mShowForced) { @@ -603,7 +638,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } return flags; } - + private int getAppShowFlags() { int flags = 0; if (mShowForced) { @@ -613,7 +648,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } return flags; } - + InputBindResult attachNewInputLocked(boolean initial, boolean needResult) { if (!mBoundToMethod) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( @@ -629,14 +664,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute)); } if (mShowRequested) { - if (DEBUG) Log.v(TAG, "Attach new input asks to show input"); + if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); showCurrentInputLocked(getAppShowFlags(), null); } return needResult ? new InputBindResult(session.session, mCurId, mCurSeq) : null; } - + InputBindResult startInputLocked(IInputMethodClient client, IInputContext inputContext, EditorInfo attribute, boolean initial, boolean needResult) { @@ -644,13 +679,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurMethodId == null) { return mNoBinding; } - + ClientState cs = mClients.get(client.asBinder()); if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); } - + try { if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) { // Check with the window manager to make sure this client actually @@ -658,18 +693,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // because if the focus changes some time before or after, the // next client receiving focus that has any interest in input will // be calling through here after that change happens. - Log.w(TAG, "Starting input on non-focused client " + cs.client + Slog.w(TAG, "Starting input on non-focused client " + cs.client + " (uid=" + cs.uid + " pid=" + cs.pid + ")"); return null; } } catch (RemoteException e) { } - + if (mCurClient != cs) { // If the client is changing, we need to switch over to the new // one. unbindCurrentClientLocked(); - if (DEBUG) Log.v(TAG, "switching to client: client = " + if (DEBUG) Slog.v(TAG, "switching to client: client = " + cs.client.asBinder()); // If the screen is on, inform the new client it is active @@ -677,19 +712,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { cs.client.setActive(mScreenOn); } catch (RemoteException e) { - Log.w(TAG, "Got RemoteException sending setActive notification to pid " + Slog.w(TAG, "Got RemoteException sending setActive notification to pid " + cs.pid + " uid " + cs.uid); } } } - + // Bump up the sequence for this client and attach it. mCurSeq++; if (mCurSeq <= 0) mCurSeq = 1; mCurClient = cs; mCurInputContext = inputContext; mCurAttribute = attribute; - + // Check if the input method is changing. if (mCurId != null && mCurId.equals(mCurMethodId)) { if (cs.curSession != null) { @@ -701,7 +736,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurMethod != null) { if (!cs.sessionRequested) { cs.sessionRequested = true; - if (DEBUG) Log.v(TAG, "Creating new session for client " + cs); + if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs); executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_CREATE_SESSION, mCurMethod, new MethodCallback(mCurMethod))); @@ -720,33 +755,33 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // 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); + EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, + mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0); } } } - + return startInputInnerLocked(); } - + InputBindResult startInputInnerLocked() { if (mCurMethodId == null) { return mNoBinding; } - + if (!mSystemReady) { // If the system is not yet ready, we shouldn't be running third // party code. return new InputBindResult(null, mCurMethodId, mCurSeq); } - + InputMethodInfo info = mMethodMap.get(mCurMethodId); if (info == null) { throw new IllegalArgumentException("Unknown id: " + mCurMethodId); } - + unbindCurrentMethodLocked(false); - + mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE); mCurIntent.setComponent(info.getComponent()); mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, @@ -759,7 +794,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurId = info.getId(); mCurToken = new Binder(); try { - if (DEBUG) Log.v(TAG, "Adding window token: " + mCurToken); + if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurToken); mIWindowManager.addWindowToken(mCurToken, WindowManager.LayoutParams.TYPE_INPUT_METHOD); } catch (RemoteException e) { @@ -767,12 +802,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return new InputBindResult(null, mCurId, mCurSeq); } else { mCurIntent = null; - Log.w(TAG, "Failure connecting to input method service: " + Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent); } return null; } - + public InputBindResult startInput(IInputMethodClient client, IInputContext inputContext, EditorInfo attribute, boolean initial, boolean needResult) { @@ -786,24 +821,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } - + public void finishInput(IInputMethodClient client) { } - + public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mMethodMap) { if (mCurIntent != null && name.equals(mCurIntent.getComponent())) { mCurMethod = IInputMethod.Stub.asInterface(service); if (mCurToken == null) { - Log.w(TAG, "Service connected without a token!"); + Slog.w(TAG, "Service connected without a token!"); unbindCurrentMethodLocked(false); return; } - if (DEBUG) Log.v(TAG, "Initiating attach with token: " + mCurToken); + if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_ATTACH_TOKEN, mCurMethod, mCurToken)); if (mCurClient != null) { - if (DEBUG) Log.v(TAG, "Creating first session while with client " + if (DEBUG) Slog.v(TAG, "Creating first session while with client " + mCurClient); executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_CREATE_SESSION, mCurMethod, @@ -830,25 +865,25 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } - + void unbindCurrentMethodLocked(boolean reportToClient) { if (mHaveConnection) { mContext.unbindService(this); mHaveConnection = false; } - + if (mCurToken != null) { try { - if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken); + if (DEBUG) Slog.v(TAG, "Removing window token: " + mCurToken); mIWindowManager.removeWindowToken(mCurToken); } catch (RemoteException e) { } mCurToken = null; } - + mCurId = null; clearCurMethodLocked(); - + if (reportToClient && mCurClient != null) { executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); @@ -860,7 +895,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { sessionState.session.finishSession(); } catch (RemoteException e) { - Log.w(TAG, "Session failed to close due to remote exception", e); + Slog.w(TAG, "Session failed to close due to remote exception", e); } } } @@ -879,10 +914,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } mStatusBar.setIconVisibility(mInputMethodIcon, false); } - + public void onServiceDisconnected(ComponentName name) { synchronized (mMethodMap) { - if (DEBUG) Log.v(TAG, "Service disconnected: " + name + if (DEBUG) Slog.v(TAG, "Service disconnected: " + name + " mCurIntent=" + mCurIntent); if (mCurMethod != null && mCurIntent != null && name.equals(mCurIntent.getComponent())) { @@ -904,16 +939,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub long ident = Binder.clearCallingIdentity(); try { if (token == null || mCurToken != token) { - Log.w(TAG, "Ignoring setInputMethod of token: " + token); + Slog.w(TAG, "Ignoring setInputMethod of token: " + token); return; } - + synchronized (mMethodMap) { if (iconId == 0) { - if (DEBUG) Log.d(TAG, "hide the small icon for the input method"); + if (DEBUG) Slog.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"); + if (DEBUG) Slog.d(TAG, "show a small icon for the input method"); mInputMethodData.iconId = iconId; mInputMethodData.iconPackage = packageName; mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null); @@ -936,7 +971,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { setInputMethodLocked(id); } catch (IllegalArgumentException e) { - Log.w(TAG, "Unknown input method from prefs: " + id, e); + Slog.w(TAG, "Unknown input method from prefs: " + id, e); mCurMethodId = null; unbindCurrentMethodLocked(true); } @@ -946,17 +981,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub unbindCurrentMethodLocked(true); } } - + void setInputMethodLocked(String id) { InputMethodInfo info = mMethodMap.get(id); if (info == null) { throw new IllegalArgumentException("Unknown id: " + mCurMethodId); } - + if (id.equals(mCurMethodId)) { return; } - + final long ident = Binder.clearCallingIdentity(); try { mCurMethodId = id; @@ -965,6 +1000,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (ActivityManagerNative.isSystemReady()) { Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra("input_method_id", id); mContext.sendBroadcast(intent); } @@ -973,7 +1009,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Binder.restoreCallingIdentity(ident); } } - + public boolean showSoftInput(IInputMethodClient client, int flags, ResultReceiver resultReceiver) { long ident = Binder.clearCallingIdentity(); @@ -986,22 +1022,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // focus in the window manager, to allow this call to // be made before input is started in it. if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring showSoftInput of: " + client); + Slog.w(TAG, "Ignoring showSoftInput of: " + client); return false; } } catch (RemoteException e) { return false; } } - - if (DEBUG) Log.v(TAG, "Client requesting input be shown"); + + if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); return showCurrentInputLocked(flags, resultReceiver); } } finally { Binder.restoreCallingIdentity(ident); } } - + boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) { mShowRequested = true; if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) { @@ -1011,11 +1047,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mShowExplicitlyRequested = true; mShowForced = true; } - + if (!mSystemReady) { return false; } - + boolean res = false; if (mCurMethod != null) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO( @@ -1029,15 +1065,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // 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, + EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime,1); mContext.unbindService(this); mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE); } - + return res; } - + public boolean hideSoftInput(IInputMethodClient client, int flags, ResultReceiver resultReceiver) { long ident = Binder.clearCallingIdentity(); @@ -1050,31 +1086,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // focus in the window manager, to allow this call to // be made before input is started in it. if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring hideSoftInput of: " + client); + Slog.w(TAG, "Ignoring hideSoftInput of: " + client); return false; } } catch (RemoteException e) { return false; } } - - if (DEBUG) Log.v(TAG, "Client requesting input be hidden"); + + if (DEBUG) Slog.v(TAG, "Client requesting input be hidden"); return hideCurrentInputLocked(flags, resultReceiver); } } finally { Binder.restoreCallingIdentity(ident); } } - + boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) { if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 && (mShowExplicitlyRequested || mShowForced)) { - if (DEBUG) Log.v(TAG, + if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide"); return false; } if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) { - if (DEBUG) Log.v(TAG, + if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide"); return false; } @@ -1092,20 +1128,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mShowForced = false; return res; } - + public void windowGainedFocus(IInputMethodClient client, IBinder windowToken, boolean viewHasFocus, boolean isTextEditor, int softInputMode, boolean first, int windowFlags) { long ident = Binder.clearCallingIdentity(); try { synchronized (mMethodMap) { - if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder() + if (DEBUG) Slog.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 { @@ -1113,19 +1149,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // focus in the window manager, to allow this call to // be made before input is started in it. if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Client not active, ignoring focus gain of: " + client); + Slog.w(TAG, "Client not active, ignoring focus gain of: " + client); return; } } catch (RemoteException e) { } } - + if (mCurFocusedWindow == windowToken) { - Log.w(TAG, "Window already focused, ignoring focus gain of: " + client); + Slog.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 & @@ -1135,7 +1171,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // 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"); + if (DEBUG) Slog.v(TAG, "Unspecified window will hide input"); hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null); } } else if (isTextEditor && (softInputMode & @@ -1145,7 +1181,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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"); + if (DEBUG) Slog.v(TAG, "Unspecified window will show input"); showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); } break; @@ -1155,23 +1191,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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"); + if (DEBUG) Slog.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"); + if (DEBUG) Slog.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"); + if (DEBUG) Slog.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"); + if (DEBUG) Slog.v(TAG, "Window asks to always show input"); showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); break; } @@ -1180,12 +1216,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Binder.restoreCallingIdentity(ident); } } - + public void showInputMethodPickerFromClient(IInputMethodClient client) { synchronized (mMethodMap) { if (mCurClient == null || client == null || mCurClient.client.asBinder() != client.asBinder()) { - Log.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client); + Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client); } mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER); @@ -1203,7 +1239,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + android.Manifest.permission.WRITE_SECURE_SETTINGS); } } else if (mCurToken != token) { - Log.w(TAG, "Ignoring setInputMethod of token: " + token); + Slog.w(TAG, "Ignoring setInputMethod of token: " + token); return; } @@ -1219,7 +1255,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void hideMySoftInput(IBinder token, int flags) { synchronized (mMethodMap) { if (token == null || mCurToken != token) { - Log.w(TAG, "Ignoring hideInputMethod of token: " + token); + Slog.w(TAG, "Ignoring hideInputMethod of token: " + token); return; } long ident = Binder.clearCallingIdentity(); @@ -1230,11 +1266,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } - + public void showMySoftInput(IBinder token, int flags) { synchronized (mMethodMap) { if (token == null || mCurToken != token) { - Log.w(TAG, "Ignoring hideInputMethod of token: " + token); + Slog.w(TAG, "Ignoring hideInputMethod of token: " + token); return; } long ident = Binder.clearCallingIdentity(); @@ -1250,7 +1286,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mEnabledSession != session) { if (mEnabledSession != null) { try { - if (DEBUG) Log.v(TAG, "Disabling: " + mEnabledSession); + if (DEBUG) Slog.v(TAG, "Disabling: " + mEnabledSession); mEnabledSession.method.setSessionEnabled( mEnabledSession.session, false); } catch (RemoteException e) { @@ -1258,23 +1294,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } mEnabledSession = session; try { - if (DEBUG) Log.v(TAG, "Enabling: " + mEnabledSession); + if (DEBUG) Slog.v(TAG, "Enabling: " + mEnabledSession); session.method.setSessionEnabled( session.session, true); } catch (RemoteException e) { } } } - + public boolean handleMessage(Message msg) { HandlerCaller.SomeArgs args; switch (msg.what) { case MSG_SHOW_IM_PICKER: showInputMethodMenu(); return true; - + // --------------------------------------------------------- - + case MSG_UNBIND_INPUT: try { ((IInputMethod)msg.obj).unbindInput(); @@ -1308,7 +1344,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case MSG_ATTACH_TOKEN: args = (HandlerCaller.SomeArgs)msg.obj; try { - if (DEBUG) Log.v(TAG, "Sending attach of token: " + args.arg2); + if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2); ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2); } catch (RemoteException e) { } @@ -1322,7 +1358,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } return true; // --------------------------------------------------------- - + case MSG_START_INPUT: args = (HandlerCaller.SomeArgs)msg.obj; try { @@ -1343,9 +1379,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (RemoteException e) { } return true; - + // --------------------------------------------------------- - + case MSG_UNBIND_METHOD: try { ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1); @@ -1359,7 +1395,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub ((IInputMethodClient)args.arg1).onBindMethod( (InputBindResult)args.arg2); } catch (RemoteException e) { - Log.w(TAG, "Client died receiving input method " + args.arg2); + Slog.w(TAG, "Client died receiving input method " + args.arg2); } return true; } @@ -1371,12 +1407,21 @@ public class InputMethodManagerService extends IInputMethodManager.Stub & ApplicationInfo.FLAG_SYSTEM) != 0; } - private boolean chooseNewDefaultIME() { + private boolean chooseNewDefaultIMELocked() { List<InputMethodInfo> enabled = getEnabledInputMethodListLocked(); if (enabled != null && enabled.size() > 0) { + // We'd prefer to fall back on a system IME, since that is safer. + int i=enabled.size(); + while (i > 0) { + i--; + if ((enabled.get(i).getServiceInfo().applicationInfo.flags + & ApplicationInfo.FLAG_SYSTEM) != 0) { + break; + } + } Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD, - enabled.get(0).getId()); + enabled.get(i).getId()); return true; } @@ -1387,153 +1432,174 @@ public class InputMethodManagerService extends IInputMethodManager.Stub HashMap<String, InputMethodInfo> map) { list.clear(); map.clear(); - + PackageManager pm = mContext.getPackageManager(); + final Configuration config = mContext.getResources().getConfiguration(); + final boolean haveHardKeyboard = config.keyboard == Configuration.KEYBOARD_QWERTY; + String disabledSysImes = Settings.Secure.getString(mContext.getContentResolver(), + Secure.DISABLED_SYSTEM_INPUT_METHODS); + if (disabledSysImes == null) disabledSysImes = ""; List<ResolveInfo> services = pm.queryIntentServices( new Intent(InputMethod.SERVICE_INTERFACE), PackageManager.GET_META_DATA); - + for (int i = 0; i < services.size(); ++i) { ResolveInfo ri = services.get(i); ServiceInfo si = ri.serviceInfo; ComponentName compName = new ComponentName(si.packageName, si.name); if (!android.Manifest.permission.BIND_INPUT_METHOD.equals( si.permission)) { - Log.w(TAG, "Skipping input method " + compName + Slog.w(TAG, "Skipping input method " + compName + ": it does not require the permission " + android.Manifest.permission.BIND_INPUT_METHOD); continue; } - if (DEBUG) Log.d(TAG, "Checking " + compName); + if (DEBUG) Slog.d(TAG, "Checking " + compName); try { InputMethodInfo p = new InputMethodInfo(mContext, ri); list.add(p); - map.put(p.getId(), p); + final String id = p.getId(); + map.put(id, p); - // System IMEs are enabled by default - if (isSystemIme(p)) { - setInputMethodEnabled(p.getId(), true); + // System IMEs are enabled by default, unless there's a hard keyboard + // and the system IME was explicitly disabled + if (isSystemIme(p) && (!haveHardKeyboard || disabledSysImes.indexOf(id) < 0)) { + setInputMethodEnabledLocked(id, true); } if (DEBUG) { - Log.d(TAG, "Found a third-party input method " + p); + Slog.d(TAG, "Found a third-party input method " + p); } - + } catch (XmlPullParserException e) { - Log.w(TAG, "Unable to load input method " + compName, e); + Slog.w(TAG, "Unable to load input method " + compName, e); } catch (IOException e) { - Log.w(TAG, "Unable to load input method " + compName, e); + Slog.w(TAG, "Unable to load input method " + compName, e); } } String defaultIme = Settings.Secure.getString(mContext .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); if (!map.containsKey(defaultIme)) { - if (chooseNewDefaultIME()) { + if (chooseNewDefaultIMELocked()) { updateFromSettingsLocked(); } } } - + // ---------------------------------------------------------------------- - + void showInputMethodMenu() { - if (DEBUG) Log.v(TAG, "Show switching menu"); + if (DEBUG) Slog.v(TAG, "Show switching menu"); - hideInputMethodMenu(); - final Context context = mContext; - + final PackageManager pm = context.getPackageManager(); - + String lastInputMethodId = Settings.Secure.getString(context .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); - if (DEBUG) Log.v(TAG, "Current IME: " + lastInputMethodId); - - final List<InputMethodInfo> immis = getEnabledInputMethodList(); - - int N = (immis == null ? 0 : immis.size()); + if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId); - mItems = new CharSequence[N]; - mIms = new InputMethodInfo[N]; + final List<InputMethodInfo> immis = getEnabledInputMethodList(); - for (int i = 0; i < N; ++i) { - InputMethodInfo property = immis.get(i); - mItems[i] = property.loadLabel(pm); - mIms[i] = property; + if (immis == null) { + return; } + + synchronized (mMethodMap) { + hideInputMethodMenuLocked(); - int checkedItem = 0; - for (int i = 0; i < N; ++i) { - if (mIms[i].getId().equals(lastInputMethodId)) { - checkedItem = i; - break; + int N = immis.size(); + + mItems = new CharSequence[N]; + mIms = new InputMethodInfo[N]; + + int j = 0; + for (int i = 0; i < N; ++i) { + InputMethodInfo property = immis.get(i); + if (property == null) { + continue; + } + mItems[j] = property.loadLabel(pm); + mIms[j] = property; + j++; } - } - - AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - hideInputMethodMenu(); + + int checkedItem = 0; + for (int i = 0; i < N; ++i) { + if (mIms[i].getId().equals(lastInputMethodId)) { + checkedItem = i; + break; + } } - }; - - TypedArray a = context.obtainStyledAttributes(null, - com.android.internal.R.styleable.DialogPreference, - com.android.internal.R.attr.alertDialogStyle, 0); - mDialogBuilder = new AlertDialog.Builder(context) - .setTitle(com.android.internal.R.string.select_input_method) - .setOnCancelListener(new OnCancelListener() { - public void onCancel(DialogInterface dialog) { - hideInputMethodMenu(); - } - }) - .setIcon(a.getDrawable( - com.android.internal.R.styleable.DialogPreference_dialogTitle)); - a.recycle(); - - mDialogBuilder.setSingleChoiceItems(mItems, checkedItem, - new AlertDialog.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - synchronized (mMethodMap) { - InputMethodInfo im = mIms[which]; + + AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + hideInputMethodMenu(); + } + }; + + TypedArray a = context.obtainStyledAttributes(null, + com.android.internal.R.styleable.DialogPreference, + com.android.internal.R.attr.alertDialogStyle, 0); + mDialogBuilder = new AlertDialog.Builder(context) + .setTitle(com.android.internal.R.string.select_input_method) + .setOnCancelListener(new OnCancelListener() { + public void onCancel(DialogInterface dialog) { hideInputMethodMenu(); - setInputMethodLocked(im.getId()); } - } - }); + }) + .setIcon(a.getDrawable( + com.android.internal.R.styleable.DialogPreference_dialogTitle)); + a.recycle(); + + mDialogBuilder.setSingleChoiceItems(mItems, checkedItem, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + synchronized (mMethodMap) { + if (mIms == null || mIms.length <= which) { + return; + } + InputMethodInfo im = mIms[which]; + hideInputMethodMenu(); + if (im != null) { + setInputMethodLocked(im.getId()); + } + } + } + }); - synchronized (mMethodMap) { mSwitchingDialog = mDialogBuilder.create(); mSwitchingDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG); mSwitchingDialog.show(); } } - + void hideInputMethodMenu() { synchronized (mMethodMap) { hideInputMethodMenuLocked(); } } - + void hideInputMethodMenuLocked() { - if (DEBUG) Log.v(TAG, "Hide switching menu"); + if (DEBUG) Slog.v(TAG, "Hide switching menu"); if (mSwitchingDialog != null) { mSwitchingDialog.dismiss(); mSwitchingDialog = null; } - + mDialogBuilder = null; mItems = null; mIms = null; } - + // ---------------------------------------------------------------------- - + public boolean setInputMethodEnabled(String id, boolean enabled) { synchronized (mMethodMap) { if (mContext.checkCallingOrSelfPermission( @@ -1546,96 +1612,100 @@ public class InputMethodManagerService extends IInputMethodManager.Stub long ident = Binder.clearCallingIdentity(); try { - // Make sure this is a valid input method. - InputMethodInfo imm = mMethodMap.get(id); - if (imm == null) { - 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; - } - // 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)) { - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD, - firstId != null ? firstId : ""); + return setInputMethodEnabledLocked(id, enabled); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + boolean setInputMethodEnabledLocked(String id, boolean enabled) { + // Make sure this is a valid input method. + InputMethodInfo imm = mMethodMap.get(id); + if (imm == null) { + 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; } - // 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); } - - // 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. + } + } + + 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; - } finally { - Binder.restoreCallingIdentity(ident); } + // 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)) { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, + firstId != null ? firstId : ""); + } + // 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; + } + + 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.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { - + pw.println("Permission Denial: can't dump InputMethodManager from from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); @@ -1644,9 +1714,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub IInputMethod method; ClientState client; - + final Printer p = new PrintWriterPrinter(pw); - + synchronized (mMethodMap) { p.println("Current Input Method Manager state:"); int N = mMethodList.size(); @@ -1683,7 +1753,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " mInputShown=" + mInputShown); p.println(" mSystemReady=" + mSystemReady + " mScreenOn=" + mScreenOn); } - + if (client != null) { p.println(" "); pw.flush(); @@ -1693,7 +1763,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub p.println("Input method client dead: " + e); } } - + if (method != null) { p.println(" "); pw.flush(); diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java index fe3ad15..2eaa58c 100644 --- a/services/java/com/android/server/Installer.java +++ b/services/java/com/android/server/Installer.java @@ -20,7 +20,7 @@ import android.content.pm.PackageStats; import android.net.LocalSocketAddress; import android.net.LocalSocket; import android.util.Config; -import android.util.Log; +import android.util.Slog; import java.io.IOException; import java.io.InputStream; @@ -41,7 +41,7 @@ class Installer { if (mSocket != null) { return true; } - Log.i(TAG, "connecting..."); + Slog.i(TAG, "connecting..."); try { mSocket = new LocalSocket(); @@ -60,7 +60,7 @@ class Installer { } private void disconnect() { - Log.i(TAG,"disconnecting..."); + Slog.i(TAG,"disconnecting..."); try { if (mSocket != null) mSocket.close(); } catch (IOException ex) { } @@ -82,16 +82,16 @@ class Installer { try { count = mIn.read(buffer, off, len - off); if (count <= 0) { - Log.e(TAG, "read error " + count); + Slog.e(TAG, "read error " + count); break; } off += count; } catch (IOException ex) { - Log.e(TAG,"read exception"); + Slog.e(TAG,"read exception"); break; } } -// Log.i(TAG, "read "+len+" bytes"); +// Slog.i(TAG, "read "+len+" bytes"); if (off == len) return true; disconnect(); return false; @@ -103,7 +103,7 @@ class Installer { if (!readBytes(buf, 2)) return false; len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); if ((len < 1) || (len > 1024)) { - Log.e(TAG,"invalid reply length ("+len+")"); + Slog.e(TAG,"invalid reply length ("+len+")"); disconnect(); return false; } @@ -122,7 +122,7 @@ class Installer { mOut.write(buf, 0, 2); mOut.write(cmd, 0, len); } catch (IOException ex) { - Log.e(TAG,"write error"); + Slog.e(TAG,"write error"); disconnect(); return false; } @@ -131,7 +131,7 @@ class Installer { private synchronized String transaction(String cmd) { if (!connect()) { - Log.e(TAG, "connection failed"); + Slog.e(TAG, "connection failed"); return "-1"; } @@ -141,18 +141,18 @@ class Installer { * write (this one). Try to reconnect and write * the command one more time before giving up. */ - Log.e(TAG, "write command failed? reconnect!"); + Slog.e(TAG, "write command failed? reconnect!"); if (!connect() || !writeCommand(cmd)) { return "-1"; } } -// Log.i(TAG,"send: '"+cmd+"'"); +// Slog.i(TAG,"send: '"+cmd+"'"); if (readReply()) { String s = new String(buf, 0, buflen); -// Log.i(TAG,"recv: '"+s+"'"); +// Slog.i(TAG,"recv: '"+s+"'"); return s; } else { -// Log.i(TAG,"fail"); +// Slog.i(TAG,"fail"); return "-1"; } } @@ -210,6 +210,15 @@ class Installer { return execute(builder.toString()); } + public int rename(String oldname, String newname) { + StringBuilder builder = new StringBuilder("rename"); + builder.append(' '); + builder.append(oldname); + builder.append(' '); + builder.append(newname); + return execute(builder.toString()); + } + public int deleteCacheFiles(String name) { StringBuilder builder = new StringBuilder("rmcache"); builder.append(' '); @@ -239,10 +248,15 @@ class Installer { return execute(builder.toString()); } - public int setForwardLockPerm(String packageName, int gid) { + /* + * @param packagePathSuffix The name of the path relative to install + * directory. Say if the path name is /data/app/com.test-1.apk, + * the package suffix path will be com.test-1 + */ + public int setForwardLockPerm(String packagePathSuffix, int gid) { StringBuilder builder = new StringBuilder("protect"); builder.append(' '); - builder.append(packageName); + builder.append(packagePathSuffix); builder.append(' '); builder.append(gid); return execute(builder.toString()); @@ -257,7 +271,6 @@ class Installer { builder.append(apkPath); builder.append(' '); builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); - String s = transaction(builder.toString()); String res[] = s.split(" "); @@ -273,4 +286,8 @@ class Installer { return -1; } } + + public int moveFiles() { + return execute("movefiles"); + } } diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index d8c8c90..8ab65e9 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Set; import android.util.Log; +import android.util.Slog; import android.util.LogPrinter; import android.util.Printer; @@ -46,9 +47,9 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { public void addFilter(F f) { if (localLOGV) { - Log.v(TAG, "Adding filter: " + f); - f.dump(new LogPrinter(Log.VERBOSE, TAG), " "); - Log.v(TAG, " Building Lookup Maps:"); + Slog.v(TAG, "Adding filter: " + f); + f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); + Slog.v(TAG, " Building Lookup Maps:"); } mFilters.add(f); @@ -72,9 +73,9 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { void removeFilterInternal(F f) { if (localLOGV) { - Log.v(TAG, "Removing filter: " + f); - f.dump(new LogPrinter(Log.VERBOSE, TAG), " "); - Log.v(TAG, " Cleaning Lookup Maps:"); + Slog.v(TAG, "Removing filter: " + f); + f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); + Slog.v(TAG, " Cleaning Lookup Maps:"); } int numS = unregister_intent_filter(f, f.schemesIterator(), @@ -90,38 +91,64 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { } } - void dumpMap(PrintWriter out, String prefix, Map<String, ArrayList<F>> map) { + boolean dumpMap(PrintWriter out, String titlePrefix, String title, + String prefix, Map<String, ArrayList<F>> map, String packageName) { String eprefix = prefix + " "; String fprefix = prefix + " "; + boolean printedSomething = false; for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) { - out.print(eprefix); out.print(e.getKey()); out.println(":"); ArrayList<F> a = e.getValue(); final int N = a.size(); + boolean printedHeader = false; for (int i=0; i<N; i++) { - dumpFilter(out, fprefix, a.get(i)); + F filter = a.get(i); + if (packageName != null && !packageName.equals(packageForFilter(filter))) { + continue; + } + if (title != null) { + out.print(titlePrefix); out.println(title); + title = null; + } + if (!printedHeader) { + out.print(eprefix); out.print(e.getKey()); out.println(":"); + printedHeader = true; + } + printedSomething = true; + dumpFilter(out, fprefix, filter); } } + return printedSomething; } - public void dump(PrintWriter out, String prefix) { + public boolean dump(PrintWriter out, String title, String prefix, String packageName) { String innerPrefix = prefix + " "; - out.print(prefix); out.println("Full MIME Types:"); - dumpMap(out, innerPrefix, mTypeToFilter); - out.println(" "); - out.print(prefix); out.println("Base MIME Types:"); - dumpMap(out, innerPrefix, mBaseTypeToFilter); - out.println(" "); - out.print(prefix); out.println("Wild MIME Types:"); - dumpMap(out, innerPrefix, mWildTypeToFilter); - out.println(" "); - out.print(prefix); out.println("Schemes:"); - dumpMap(out, innerPrefix, mSchemeToFilter); - out.println(" "); - out.print(prefix); out.println("Non-Data Actions:"); - dumpMap(out, innerPrefix, mActionToFilter); - out.println(" "); - out.print(prefix); out.println("MIME Typed Actions:"); - dumpMap(out, innerPrefix, mTypedActionToFilter); + String sepPrefix = "\n" + prefix; + String curPrefix = title + "\n" + prefix; + if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix, + mTypeToFilter, packageName)) { + curPrefix = sepPrefix; + } + if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix, + mBaseTypeToFilter, packageName)) { + curPrefix = sepPrefix; + } + if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix, + mWildTypeToFilter, packageName)) { + curPrefix = sepPrefix; + } + if (dumpMap(out, curPrefix, "Schemes:", innerPrefix, + mSchemeToFilter, packageName)) { + curPrefix = sepPrefix; + } + if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix, + mActionToFilter, packageName)) { + curPrefix = sepPrefix; + } + if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix, + mTypedActionToFilter, packageName)) { + curPrefix = sepPrefix; + } + return curPrefix == sepPrefix; } private class IteratorWrapper implements Iterator<F> { @@ -188,7 +215,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { final boolean debug = localLOGV || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); - if (debug) Log.v( + if (debug) Slog.v( TAG, "Resolving type " + resolvedType + " scheme " + scheme + " of intent " + intent); @@ -209,26 +236,26 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { // Not a wild card, so we can just look for all filters that // completely match or wildcards whose base type matches. firstTypeCut = mTypeToFilter.get(resolvedType); - if (debug) Log.v(TAG, "First type cut: " + firstTypeCut); + if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut); secondTypeCut = mWildTypeToFilter.get(baseType); - if (debug) Log.v(TAG, "Second type cut: " + secondTypeCut); + if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut); } else { // We can match anything with our base type. firstTypeCut = mBaseTypeToFilter.get(baseType); - if (debug) Log.v(TAG, "First type cut: " + firstTypeCut); + if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut); secondTypeCut = mWildTypeToFilter.get(baseType); - if (debug) Log.v(TAG, "Second type cut: " + secondTypeCut); + if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut); } // Any */* types always apply, but we only need to do this // if the intent type was not already */*. thirdTypeCut = mWildTypeToFilter.get("*"); - if (debug) Log.v(TAG, "Third type cut: " + thirdTypeCut); + if (debug) Slog.v(TAG, "Third type cut: " + thirdTypeCut); } else if (intent.getAction() != null) { // The intent specified any type ({@literal *}/*). This // can be a whole heck of a lot of things, so as a first // cut let's use the action instead. firstTypeCut = mTypedActionToFilter.get(intent.getAction()); - if (debug) Log.v(TAG, "Typed Action list: " + firstTypeCut); + if (debug) Slog.v(TAG, "Typed Action list: " + firstTypeCut); } } } @@ -238,7 +265,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { // on the authority and path by directly matching each resulting filter). if (scheme != null) { schemeCut = mSchemeToFilter.get(scheme); - if (debug) Log.v(TAG, "Scheme list: " + schemeCut); + if (debug) Slog.v(TAG, "Scheme list: " + schemeCut); } // If the intent does not specify any data -- either a MIME type or @@ -246,7 +273,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { // data. if (resolvedType == null && scheme == null && intent.getAction() != null) { firstTypeCut = mActionToFilter.get(intent.getAction()); - if (debug) Log.v(TAG, "Action list: " + firstTypeCut); + if (debug) Slog.v(TAG, "Action list: " + firstTypeCut); } if (firstTypeCut != null) { @@ -268,9 +295,9 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { sortResults(finalList); if (debug) { - Log.v(TAG, "Final result list:"); + Slog.v(TAG, "Final result list:"); for (R r : finalList) { - Log.v(TAG, " " + r); + Slog.v(TAG, " " + r); } } return finalList; @@ -285,6 +312,10 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { return true; } + protected String packageForFilter(F filter) { + return null; + } + protected R newResult(F filter, int match) { return (R)filter; } @@ -307,7 +338,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { while (i.hasNext()) { String name = (String)i.next(); num++; - if (localLOGV) Log.v(TAG, prefix + name); + if (localLOGV) Slog.v(TAG, prefix + name); String baseName = name; final int slashpos = name.indexOf('/'); if (slashpos > 0) { @@ -318,7 +349,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { ArrayList<F> array = mTypeToFilter.get(name); if (array == null) { - //Log.v(TAG, "Creating new array for " + name); + //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); mTypeToFilter.put(name, array); } @@ -327,7 +358,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { if (slashpos > 0) { array = mBaseTypeToFilter.get(baseName); if (array == null) { - //Log.v(TAG, "Creating new array for " + name); + //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); mBaseTypeToFilter.put(baseName, array); } @@ -335,7 +366,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { } else { array = mWildTypeToFilter.get(baseName); if (array == null) { - //Log.v(TAG, "Creating new array for " + name); + //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); mWildTypeToFilter.put(baseName, array); } @@ -356,7 +387,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { while (i.hasNext()) { String name = (String)i.next(); num++; - if (localLOGV) Log.v(TAG, prefix + name); + if (localLOGV) Slog.v(TAG, prefix + name); String baseName = name; final int slashpos = name.indexOf('/'); if (slashpos > 0) { @@ -392,10 +423,10 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { while (i.hasNext()) { String name = i.next(); num++; - if (localLOGV) Log.v(TAG, prefix + name); + if (localLOGV) Slog.v(TAG, prefix + name); ArrayList<F> array = dest.get(name); if (array == null) { - //Log.v(TAG, "Creating new array for " + name); + //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); dest.put(name, array); } @@ -414,7 +445,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { while (i.hasNext()) { String name = i.next(); num++; - if (localLOGV) Log.v(TAG, prefix + name); + if (localLOGV) Slog.v(TAG, prefix + name); if (!remove_all_objects(dest.get(name), filter)) { dest.remove(name); } @@ -447,12 +478,12 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { for (i=0; i<N; i++) { F filter = src.get(i); int match; - if (debug) Log.v(TAG, "Matching against filter " + filter); + if (debug) Slog.v(TAG, "Matching against filter " + filter); // Do we already have this one? if (!allowFilterResult(filter, dest)) { if (debug) { - Log.v(TAG, " Filter's target already added"); + Slog.v(TAG, " Filter's target already added"); } continue; } @@ -460,7 +491,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { match = filter.match( intent.getAction(), resolvedType, scheme, intent.getData(), categories, TAG); if (match >= 0) { - if (debug) Log.v(TAG, " Filter matched! match=0x" + + if (debug) Slog.v(TAG, " Filter matched! match=0x" + Integer.toHexString(match)); if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) { final R oneResult = newResult(filter, match); @@ -480,13 +511,13 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; default: reason = "unknown reason"; break; } - Log.v(TAG, " Filter did not match: " + reason); + Slog.v(TAG, " Filter did not match: " + reason); } } } if (dest.size() == 0 && hasNonDefaults) { - Log.w(TAG, "resolveIntent failed: found match, but none with Intent.CATEGORY_DEFAULT"); + Slog.w(TAG, "resolveIntent failed: found match, but none with Intent.CATEGORY_DEFAULT"); } } diff --git a/services/java/com/android/server/JournaledFile.java b/services/java/com/android/server/JournaledFile.java deleted file mode 100644 index 3d1f52d..0000000 --- a/services/java/com/android/server/JournaledFile.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import java.io.File; -import java.io.IOException; - -public class JournaledFile { - File mReal; - File mTemp; - boolean mWriting; - - public JournaledFile(File real, File temp) { - mReal = real; - mTemp = temp; - } - - /** Returns the file for you to read. - * @more - * Prefers the real file. If it doesn't exist, uses the temp one, and then copies - * it to the real one. If there is both a real file and a temp one, assumes that the - * temp one isn't fully written and deletes it. - */ - public File chooseForRead() { - File result; - if (mReal.exists()) { - result = mReal; - if (mTemp.exists()) { - mTemp.delete(); - } - } else if (mTemp.exists()) { - result = mTemp; - mTemp.renameTo(mReal); - } else { - return mReal; - } - return result; - } - - /** - * Returns a file for you to write. - * @more - * If a write is already happening, throws. In other words, you must provide your - * own locking. - * <p> - * Call {@link #commit} to commit the changes, or {@link #rollback} to forget the changes. - */ - public File chooseForWrite() { - if (mWriting) { - throw new IllegalStateException("uncommitted write already in progress"); - } - if (!mReal.exists()) { - // If the real one doesn't exist, it's either because this is the first time - // or because something went wrong while copying them. In this case, we can't - // trust anything that's in temp. In order to have the chooseForRead code not - // use the temporary one until it's fully written, create an empty file - // for real, which will we'll shortly delete. - try { - mReal.createNewFile(); - } catch (IOException e) { - // Ignore - } - } - - if (mTemp.exists()) { - mTemp.delete(); - } - mWriting = true; - return mTemp; - } - - /** - * Commit changes. - */ - public void commit() { - if (!mWriting) { - throw new IllegalStateException("no file to commit"); - } - mWriting = false; - mTemp.renameTo(mReal); - } - - /** - * Roll back changes. - */ - public void rollback() { - if (!mWriting) { - throw new IllegalStateException("no file to roll back"); - } - mWriting = false; - mTemp.delete(); - } -} diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java index 1bb897b..f30346b 100644 --- a/services/java/com/android/server/KeyInputQueue.java +++ b/services/java/com/android/server/KeyInputQueue.java @@ -18,11 +18,13 @@ package com.android.server; import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; import android.os.Environment; import android.os.LatencyTimer; import android.os.PowerManager; import android.os.SystemClock; -import android.util.Log; +import android.os.SystemProperties; +import android.util.Slog; import android.util.SparseArray; import android.util.Xml; import android.view.Display; @@ -43,6 +45,7 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; +import java.io.PrintWriter; import java.util.ArrayList; public abstract class KeyInputQueue { @@ -58,6 +61,12 @@ public abstract class KeyInputQueue { */ static boolean BAD_TOUCH_HACK = false; + /** + * Turn on some hacks to improve touch interaction with another device + * where touch coordinate data can get corrupted. + */ + static boolean JUMPY_TOUCH_HACK = false; + private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>(); @@ -177,7 +186,7 @@ public abstract class KeyInputQueue { return; } - if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode + if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "computeHitRect for " + scancode + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY); lastDevice = dev; @@ -211,11 +220,11 @@ public abstract class KeyInputQueue { String str = br.readLine(); if (str != null) { String[] it = str.split(":"); - if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it); + if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it); final int N = it.length-6; for (int i=0; i<=N; i+=6) { if (!"0x01".equals(it[i])) { - Log.w(TAG, "Unknown virtual key type at elem #" + i + Slog.w(TAG, "Unknown virtual key type at elem #" + i + ": " + it[i]); continue; } @@ -226,22 +235,22 @@ public abstract class KeyInputQueue { sb.centery = Integer.parseInt(it[i+3]); sb.width = Integer.parseInt(it[i+4]); sb.height = Integer.parseInt(it[i+5]); - if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key " + if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key " + sb.scancode + ": center=" + sb.centerx + "," + sb.centery + " size=" + sb.width + "x" + sb.height); mVirtualKeys.add(sb); } catch (NumberFormatException e) { - Log.w(TAG, "Bad number at region " + i + " in: " + Slog.w(TAG, "Bad number at region " + i + " in: " + str, e); } } } br.close(); } catch (FileNotFoundException e) { - Log.i(TAG, "No virtual keys found"); + Slog.i(TAG, "No virtual keys found"); } catch (IOException e) { - Log.w(TAG, "Error reading virtual keys", e); + Slog.w(TAG, "Error reading virtual keys", e); } } @@ -264,14 +273,14 @@ public abstract class KeyInputQueue { } String name = parser.getAttributeValue(null, "name"); if (name != null) { - if (DEBUG) Log.v(TAG, "addExcludedDevice " + name); + if (DEBUG) Slog.v(TAG, "addExcludedDevice " + name); addExcludedDevice(name); } } } catch (FileNotFoundException e) { // It's ok if the file does not exist. } catch (Exception e) { - Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e); + Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e); } finally { try { if (confreader != null) confreader.close(); } catch (IOException e) { } } @@ -282,8 +291,10 @@ public abstract class KeyInputQueue { lt = new LatencyTimer(100, 1000); } - BAD_TOUCH_HACK = context.getResources().getBoolean( - com.android.internal.R.bool.config_filterTouchEvents); + Resources r = context.getResources(); + BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents); + + JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents); mHapticFeedbackCallback = hapticFeedbackCallback; @@ -326,21 +337,21 @@ public abstract class KeyInputQueue { if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { config.touchscreen = Configuration.TOUCHSCREEN_FINGER; - //Log.i("foo", "***** HAVE TOUCHSCREEN!"); + //Slog.i("foo", "***** HAVE TOUCHSCREEN!"); } if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) { config.keyboard = Configuration.KEYBOARD_QWERTY; - //Log.i("foo", "***** HAVE QWERTY!"); + //Slog.i("foo", "***** HAVE QWERTY!"); } if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) { config.navigation = Configuration.NAVIGATION_TRACKBALL; - //Log.i("foo", "***** HAVE TRACKBALL!"); + //Slog.i("foo", "***** HAVE TRACKBALL!"); } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) { config.navigation = Configuration.NAVIGATION_DPAD; - //Log.i("foo", "***** HAVE DPAD!"); + //Slog.i("foo", "***** HAVE DPAD!"); } } } @@ -491,7 +502,7 @@ public abstract class KeyInputQueue { Thread mThread = new Thread("InputDeviceReader") { public void run() { - if (DEBUG) Log.v(TAG, "InputDeviceReader.run()"); + if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()"); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); @@ -507,7 +518,7 @@ public abstract class KeyInputQueue { boolean configChanged = false; if (false) { - Log.i(TAG, "Input event: dev=0x" + Slog.i(TAG, "Input event: dev=0x" + Integer.toHexString(ev.deviceId) + " type=0x" + Integer.toHexString(ev.type) + " scancode=" + ev.scancode @@ -531,7 +542,7 @@ public abstract class KeyInputQueue { } else { // We won't do anything with this device. mIgnoredDevices.put(ev.deviceId, di); - Log.i(TAG, "Ignoring non-input device: id=0x" + Slog.i(TAG, "Ignoring non-input device: id=0x" + Integer.toHexString(di.id) + ", name=" + di.name); } @@ -539,7 +550,7 @@ public abstract class KeyInputQueue { } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) { synchronized (mFirst) { if (false) { - Log.i(TAG, "Device removed: id=0x" + Slog.i(TAG, "Device removed: id=0x" + Integer.toHexString(ev.deviceId)); } di = mDevices.get(ev.deviceId); @@ -551,7 +562,7 @@ public abstract class KeyInputQueue { } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) { mIgnoredDevices.remove(ev.deviceId); } else { - Log.w(TAG, "Removing bad device id: " + Slog.w(TAG, "Removing bad device id: " + Integer.toHexString(ev.deviceId)); continue; } @@ -591,7 +602,7 @@ public abstract class KeyInputQueue { //curTime = gotOne ? ev.when : SystemClock.uptimeMillis(); final long curTime = SystemClock.uptimeMillis(); final long curTimeNano = System.nanoTime(); - //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis()); + //Slog.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis()); final int classes = di.classes; final int type = ev.type; @@ -646,14 +657,14 @@ public abstract class KeyInputQueue { di.mAbs.changed = true; di.mAbs.mNextData[di.mAbs.mAddingPointerOffset + MotionEvent.SAMPLE_X] = ev.value; - if (DEBUG_POINTERS) Log.v(TAG, "MT @" + if (DEBUG_POINTERS) Slog.v(TAG, "MT @" + di.mAbs.mAddingPointerOffset + " X:" + ev.value); } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) { di.mAbs.changed = true; di.mAbs.mNextData[di.mAbs.mAddingPointerOffset + MotionEvent.SAMPLE_Y] = ev.value; - if (DEBUG_POINTERS) Log.v(TAG, "MT @" + if (DEBUG_POINTERS) Slog.v(TAG, "MT @" + di.mAbs.mAddingPointerOffset + " Y:" + ev.value); } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) { @@ -711,7 +722,7 @@ public abstract class KeyInputQueue { + MotionEvent.SAMPLE_PRESSURE] != 0) { final int num = di.mAbs.mNextNumPointers+1; di.mAbs.mNextNumPointers = num; - if (DEBUG_POINTERS) Log.v(TAG, + if (DEBUG_POINTERS) Slog.v(TAG, "MT_REPORT: now have " + num + " pointers"); final int newOffset = (num <= InputDevice.MAX_POINTERS) ? (num * MotionEvent.NUM_SAMPLE_DATA) @@ -721,7 +732,7 @@ public abstract class KeyInputQueue { di.mAbs.mNextData[newOffset + MotionEvent.SAMPLE_PRESSURE] = 0; } else { - if (DEBUG_POINTERS) Log.v(TAG, "MT_REPORT: no pointer"); + if (DEBUG_POINTERS) Slog.v(TAG, "MT_REPORT: no pointer"); } } @@ -738,6 +749,7 @@ public abstract class KeyInputQueue { InputDevice.MotionState ms = di.mAbs; if (ms.changed) { + ms.everChanged = true; ms.changed = false; if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN @@ -755,6 +767,9 @@ public abstract class KeyInputQueue { if (BAD_TOUCH_HACK) { ms.dropBadPoint(di); } + if (JUMPY_TOUCH_HACK) { + ms.dropJumpyPoint(di); + } boolean doMotion = !monitorVirtualKey(di, ev, curTime, curTimeNano); @@ -775,14 +790,14 @@ public abstract class KeyInputQueue { me = ms.generateAbsMotion(di, curTime, curTimeNano, mDisplay, mOrientation, mGlobalMetaState); - if (DEBUG_POINTERS) Log.v(TAG, "Absolute: x=" + if (DEBUG_POINTERS) Slog.v(TAG, "Absolute: x=" + di.mAbs.mNextData[MotionEvent.SAMPLE_X] + " y=" + di.mAbs.mNextData[MotionEvent.SAMPLE_Y] + " ev=" + me); if (me != null) { if (WindowManagerPolicy.WATCH_POINTER) { - Log.i(TAG, "Enqueueing: " + me); + Slog.i(TAG, "Enqueueing: " + me); } addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_TOUCHSCREEN, me); @@ -809,12 +824,13 @@ public abstract class KeyInputQueue { ms = di.mRel; if (ms.changed) { + ms.everChanged = true; ms.changed = false; me = ms.generateRelMotion(di, curTime, curTimeNano, mOrientation, mGlobalMetaState); - if (false) Log.v(TAG, "Relative: x=" + if (false) Slog.v(TAG, "Relative: x=" + di.mRel.mNextData[MotionEvent.SAMPLE_X] + " y=" + di.mRel.mNextData[MotionEvent.SAMPLE_Y] @@ -823,15 +839,13 @@ public abstract class KeyInputQueue { addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_TRACKBALL, me); } - - ms.finish(); } } } } } catch (RuntimeException exc) { - Log.e(TAG, "InputReaderThread uncaught exception", exc); + Slog.e(TAG, "InputReaderThread uncaught exception", exc); } } } @@ -849,7 +863,7 @@ public abstract class KeyInputQueue { && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) { - if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input (" + if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Input (" + absm.mNextData[MotionEvent.SAMPLE_X] + "," + absm.mNextData[MotionEvent.SAMPLE_Y] + ") inside of display"); @@ -869,7 +883,7 @@ public abstract class KeyInputQueue { for (int i=0; i<N; i++) { VirtualKey sb = mVirtualKeys.get(i); sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight); - if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test (" + if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit test (" + absm.mNextData[MotionEvent.SAMPLE_X] + "," + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code " + sb.scancode + " - (" + sb.hitLeft @@ -877,7 +891,7 @@ public abstract class KeyInputQueue { + sb.hitBottom + ")"); if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X], absm.mNextData[MotionEvent.SAMPLE_Y])) { - if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!"); + if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit!"); return sb; } } @@ -900,7 +914,7 @@ public abstract class KeyInputQueue { vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode); ms.mLastNumPointers = ms.mNextNumPointers; di.mKeyDownTime = curTime; - if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, + if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Generate key down for: " + vk.scancode + " (keycode=" + vk.lastKeycode + ")"); KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true, @@ -926,7 +940,7 @@ public abstract class KeyInputQueue { final InputDevice.AbsoluteInfo absx = di.absX; final InputDevice.AbsoluteInfo absy = di.absY; final InputDevice.MotionState absm = di.mAbs; - Log.v(TAG, "Rejecting (" + Slog.v(TAG, "Rejecting (" + absm.mNextData[MotionEvent.SAMPLE_X] + "," + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of (" + absx.minValue + "," + absy.minValue @@ -947,7 +961,7 @@ public abstract class KeyInputQueue { if (ms.mNextNumPointers <= 0) { mPressedVirtualKey = null; ms.mLastNumPointers = 0; - if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Generate key up for: " + vk.scancode); + if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Generate key up for: " + vk.scancode); KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false, vk.lastKeycode, 0, vk.scancode, KeyEvent.FLAG_VIRTUAL_HARD_KEY); @@ -962,7 +976,7 @@ public abstract class KeyInputQueue { // virtual key and start a pointer // motion. mPressedVirtualKey = null; - if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Cancel key up for: " + vk.scancode); + if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Cancel key up for: " + vk.scancode); KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false, vk.lastKeycode, 0, vk.scancode, KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY); @@ -1138,12 +1152,12 @@ public abstract class KeyInputQueue { void recycleEvent(QueuedEvent ev) { synchronized (mFirst) { - //Log.i(TAG, "Recycle event: " + ev); + //Slog.i(TAG, "Recycle event: " + ev); if (ev.event == ev.inputDevice.mAbs.currentMove) { ev.inputDevice.mAbs.currentMove = null; } if (ev.event == ev.inputDevice.mRel.currentMove) { - if (false) Log.i(TAG, "Detach rel " + ev.event); + if (false) Slog.i(TAG, "Detach rel " + ev.event); ev.inputDevice.mRel.currentMove = null; ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0; ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0; @@ -1237,7 +1251,7 @@ public abstract class KeyInputQueue { InputDevice.AbsoluteInfo absPressure = null; InputDevice.AbsoluteInfo absSize = null; if (classes != 0) { - Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId) + Slog.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId) + ", name=" + name + ", classes=" + Integer.toHexString(classes)); if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) { @@ -1269,15 +1283,102 @@ public abstract class KeyInputQueue { InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo(); if (getAbsoluteInfo(id, channel, info) && info.minValue != info.maxValue) { - Log.i(TAG, " " + name + ": min=" + info.minValue + Slog.i(TAG, " " + name + ": min=" + info.minValue + " max=" + info.maxValue + " flat=" + info.flat + " fuzz=" + info.fuzz); info.range = info.maxValue-info.minValue; return info; } - Log.i(TAG, " " + name + ": unknown values"); + Slog.i(TAG, " " + name + ": unknown values"); return null; } private static native boolean readEvent(RawInputEvent outEvent); + + void dump(PrintWriter pw, String prefix) { + synchronized (mFirst) { + for (int i=0; i<mDevices.size(); i++) { + InputDevice dev = mDevices.valueAt(i); + pw.print(prefix); pw.print("Device #"); + pw.print(mDevices.keyAt(i)); pw.print(" "); + pw.print(dev.name); pw.print(" (classes=0x"); + pw.print(Integer.toHexString(dev.classes)); + pw.println("):"); + pw.print(prefix); pw.print(" mKeyDownTime="); + pw.print(dev.mKeyDownTime); pw.print(" mMetaKeysState="); + pw.println(dev.mMetaKeysState); + if (dev.absX != null) { + pw.print(prefix); pw.print(" absX: "); dev.absX.dump(pw); + pw.println(""); + } + if (dev.absY != null) { + pw.print(prefix); pw.print(" absY: "); dev.absY.dump(pw); + pw.println(""); + } + if (dev.absPressure != null) { + pw.print(prefix); pw.print(" absPressure: "); + dev.absPressure.dump(pw); pw.println(""); + } + if (dev.absSize != null) { + pw.print(prefix); pw.print(" absSize: "); + dev.absSize.dump(pw); pw.println(""); + } + if (dev.mAbs.everChanged) { + pw.print(prefix); pw.println(" mAbs:"); + dev.mAbs.dump(pw, prefix + " "); + } + if (dev.mRel.everChanged) { + pw.print(prefix); pw.println(" mRel:"); + dev.mRel.dump(pw, prefix + " "); + } + } + pw.println(" "); + for (int i=0; i<mIgnoredDevices.size(); i++) { + InputDevice dev = mIgnoredDevices.valueAt(i); + pw.print(prefix); pw.print("Ignored Device #"); + pw.print(mIgnoredDevices.keyAt(i)); pw.print(" "); + pw.print(dev.name); pw.print(" (classes=0x"); + pw.print(Integer.toHexString(dev.classes)); + pw.println(")"); + } + pw.println(" "); + for (int i=0; i<mVirtualKeys.size(); i++) { + VirtualKey vk = mVirtualKeys.get(i); + pw.print(prefix); pw.print("Virtual Key #"); + pw.print(i); pw.println(":"); + pw.print(prefix); pw.print(" scancode="); pw.println(vk.scancode); + pw.print(prefix); pw.print(" centerx="); pw.print(vk.centerx); + pw.print(" centery="); pw.print(vk.centery); + pw.print(" width="); pw.print(vk.width); + pw.print(" height="); pw.println(vk.height); + pw.print(prefix); pw.print(" hitLeft="); pw.print(vk.hitLeft); + pw.print(" hitTop="); pw.print(vk.hitTop); + pw.print(" hitRight="); pw.print(vk.hitRight); + pw.print(" hitBottom="); pw.println(vk.hitBottom); + if (vk.lastDevice != null) { + pw.print(prefix); pw.print(" lastDevice=#"); + pw.println(vk.lastDevice.id); + } + if (vk.lastKeycode != 0) { + pw.print(prefix); pw.print(" lastKeycode="); + pw.println(vk.lastKeycode); + } + } + pw.println(" "); + pw.print(prefix); pw.print(" Default keyboard: "); + pw.println(SystemProperties.get("hw.keyboards.0.devname")); + pw.print(prefix); pw.print(" mGlobalMetaState="); + pw.print(mGlobalMetaState); pw.print(" mHaveGlobalMetaState="); + pw.println(mHaveGlobalMetaState); + pw.print(prefix); pw.print(" mDisplayWidth="); + pw.print(mDisplayWidth); pw.print(" mDisplayHeight="); + pw.println(mDisplayHeight); + pw.print(prefix); pw.print(" mOrientation="); + pw.println(mOrientation); + if (mPressedVirtualKey != null) { + pw.print(prefix); pw.print(" mPressedVirtualKey.scancode="); + pw.println(mPressedVirtualKey.scancode); + } + } + } } diff --git a/services/java/com/android/server/LightsService.java b/services/java/com/android/server/LightsService.java new file mode 100644 index 0000000..c056eef --- /dev/null +++ b/services/java/com/android/server/LightsService.java @@ -0,0 +1,163 @@ +/* + * 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.os.Handler; +import android.os.Message; +import android.util.Slog; + +public class LightsService { + private static final String TAG = "LightsService"; + + static final int LIGHT_ID_BACKLIGHT = 0; + static final int LIGHT_ID_KEYBOARD = 1; + static final int LIGHT_ID_BUTTONS = 2; + static final int LIGHT_ID_BATTERY = 3; + static final int LIGHT_ID_NOTIFICATIONS = 4; + static final int LIGHT_ID_ATTENTION = 5; + static final int LIGHT_ID_BLUETOOTH = 6; + static final int LIGHT_ID_WIFI = 7; + static final int LIGHT_ID_COUNT = 8; + + static final int LIGHT_FLASH_NONE = 0; + static final int LIGHT_FLASH_TIMED = 1; + static final int LIGHT_FLASH_HARDWARE = 2; + + /** + * Light brightness is managed by a user setting. + */ + static final int BRIGHTNESS_MODE_USER = 0; + + /** + * Light brightness is managed by a light sensor. + */ + static final int BRIGHTNESS_MODE_SENSOR = 1; + + private final Light mLights[] = new Light[LIGHT_ID_COUNT]; + + public final class Light { + + private Light(int id) { + mId = id; + } + + public void setBrightness(int brightness) { + setBrightness(brightness, BRIGHTNESS_MODE_USER); + } + + public void setBrightness(int brightness, int brightnessMode) { + synchronized (this) { + int color = brightness & 0x000000ff; + color = 0xff000000 | (color << 16) | (color << 8) | color; + setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode); + } + } + + public void setColor(int color) { + synchronized (this) { + setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, 0); + } + } + + public void setFlashing(int color, int mode, int onMS, int offMS) { + synchronized (this) { + setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER); + } + } + + + public void pulse() { + pulse(0x00ffffff, 7); + } + + public void pulse(int color, int onMS) { + synchronized (this) { + if (mColor == 0 && !mFlashing) { + setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000, BRIGHTNESS_MODE_USER); + mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS); + } + } + } + + public void turnOff() { + synchronized (this) { + setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, 0); + } + } + + private void stopFlashing() { + synchronized (this) { + setLightLocked(mColor, LIGHT_FLASH_NONE, 0, 0, BRIGHTNESS_MODE_USER); + } + } + + private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) { + if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) { + mColor = color; + mMode = mode; + mOnMS = onMS; + mOffMS = offMS; + setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode); + } + } + + private int mId; + private int mColor; + private int mMode; + private int mOnMS; + private int mOffMS; + private boolean mFlashing; + } + + LightsService(Context context) { + + mNativePointer = init_native(); + mContext = context; + + for (int i = 0; i < LIGHT_ID_COUNT; i++) { + mLights[i] = new Light(i); + } + } + + protected void finalize() throws Throwable { + finalize_native(mNativePointer); + super.finalize(); + } + + public Light getLight(int id) { + return mLights[id]; + } + + private Handler mH = new Handler() { + @Override + public void handleMessage(Message msg) { + Light light = (Light)msg.obj; + light.stopFlashing(); + } + }; + + private static native int init_native(); + private static native void finalize_native(int ptr); + + private static native void setLight_native(int ptr, int light, int color, int mode, + int onMS, int offMS, int brightnessMode); + + private final Context mContext; + + private int mNativePointer; +} diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 024d8da..ef57056 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -27,26 +27,30 @@ import java.util.Observable; import java.util.Observer; import java.util.Set; +import android.app.Activity; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentQueryMap; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.database.Cursor; import android.location.Address; -import android.location.IGeocodeProvider; +import android.location.GeocoderParams; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; import android.location.ILocationListener; import android.location.ILocationManager; -import android.location.ILocationProvider; import android.location.INetInitiatedListener; import android.location.Location; import android.location.LocationManager; import android.location.LocationProvider; +import android.location.LocationProviderInterface; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; @@ -61,12 +65,15 @@ import android.os.Process; import android.os.RemoteException; import android.provider.Settings; import android.util.Log; +import android.util.Slog; import android.util.PrintWriterPrinter; +import com.android.internal.location.GeocoderProxy; import com.android.internal.location.GpsLocationProvider; +import com.android.internal.location.GpsNetInitiatedHandler; import com.android.internal.location.LocationProviderProxy; import com.android.internal.location.MockProvider; -import com.android.internal.location.GpsNetInitiatedHandler; +import com.android.internal.location.PassiveProvider; /** * The service class that manages LocationProviders and issues location @@ -104,14 +111,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private static boolean sProvidersLoaded = false; private final Context mContext; - private IGeocodeProvider mGeocodeProvider; + private GeocoderProxy mGeocodeProvider; private IGpsStatusProvider mGpsStatusProvider; private INetInitiatedListener mNetInitiatedListener; private LocationWorkerHandler mLocationHandler; // Cache the real providers for use in addTestProvider() and removeTestProvider() - LocationProviderProxy mNetworkLocationProvider; - LocationProviderProxy mGpsLocationProvider; + LocationProviderInterface mNetworkLocationProvider; + LocationProviderInterface mGpsLocationProvider; // Handler messages private static final int MESSAGE_LOCATION_CHANGED = 1; @@ -130,10 +137,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run /** * List of location providers. */ - private final ArrayList<LocationProviderProxy> mProviders = - new ArrayList<LocationProviderProxy>(); - private final HashMap<String, LocationProviderProxy> mProvidersByName - = new HashMap<String, LocationProviderProxy>(); + private final ArrayList<LocationProviderInterface> mProviders = + new ArrayList<LocationProviderInterface>(); + private final HashMap<String, LocationProviderInterface> mProvidersByName + = new HashMap<String, LocationProviderInterface>(); /** * Object used internally for synchronization @@ -347,7 +354,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run public void binderDied() { if (LOCAL_LOGV) { - Log.v(TAG, "Location listener died"); + Slog.v(TAG, "Location listener died"); } synchronized (mLock) { removeUpdatesLocked(this); @@ -407,14 +414,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - private void addProvider(LocationProviderProxy provider) { + private void addProvider(LocationProviderInterface provider) { mProviders.add(provider); mProvidersByName.put(provider.getName(), provider); } - private void removeProvider(LocationProviderProxy provider) { + private void removeProvider(LocationProviderInterface provider) { mProviders.remove(provider); - provider.unlinkProvider(); mProvidersByName.remove(provider.getName()); } @@ -434,7 +440,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run try { _loadProvidersLocked(); } catch (Exception e) { - Log.e(TAG, "Exception loading providers:", e); + Slog.e(TAG, "Exception loading providers:", e); } } @@ -442,12 +448,32 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Attempt to load "real" providers first if (GpsLocationProvider.isSupported()) { // Create a gps location provider - GpsLocationProvider provider = new GpsLocationProvider(mContext, this); - mGpsStatusProvider = provider.getGpsStatusProvider(); - mNetInitiatedListener = provider.getNetInitiatedListener(); - LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider); - addProvider(proxy); - mGpsLocationProvider = proxy; + GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this); + mGpsStatusProvider = gpsProvider.getGpsStatusProvider(); + mNetInitiatedListener = gpsProvider.getNetInitiatedListener(); + addProvider(gpsProvider); + mGpsLocationProvider = gpsProvider; + } + + // create a passive location provider, which is always enabled + PassiveProvider passiveProvider = new PassiveProvider(this); + addProvider(passiveProvider); + mEnabledProviders.add(passiveProvider.getName()); + + // initialize external network location and geocoder services + Resources resources = mContext.getResources(); + String serviceName = resources.getString( + com.android.internal.R.string.config_networkLocationProvider); + if (serviceName != null) { + mNetworkLocationProvider = + new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER, + serviceName, mLocationHandler); + addProvider(mNetworkLocationProvider); + } + + serviceName = resources.getString(com.android.internal.R.string.config_geocodeProvider); + if (serviceName != null) { + mGeocodeProvider = new GeocoderProxy(mContext, serviceName); } updateProvidersLocked(); @@ -460,14 +486,17 @@ public class LocationManagerService extends ILocationManager.Stub implements Run super(); mContext = context; - Thread thread = new Thread(null, this, "LocationManagerService"); - thread.start(); - if (LOCAL_LOGV) { - Log.v(TAG, "Constructed LocationManager Service"); + Slog.v(TAG, "Constructed LocationManager Service"); } } + void systemReady() { + // we defer starting up the service until the system is ready + Thread thread = new Thread(null, this, "LocationManagerService"); + thread.start(); + } + private void initialize() { // Create a wake lock, needs to be done before calling loadProviders() below PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -482,7 +511,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Register for Package Manager updates intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); + intentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); mContext.registerReceiver(mBroadcastReceiver, intentFilter); + IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + mContext.registerReceiver(mBroadcastReceiver, sdFilter); // listen for settings changes ContentResolver resolver = mContext.getContentResolver(); @@ -504,45 +536,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run Looper.loop(); } - public void installLocationProvider(String name, ILocationProvider provider) { - if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission"); - } - - synchronized (mLock) { - // check to see if we are reinstalling a dead provider - LocationProviderProxy oldProvider = mProvidersByName.get(name); - if (oldProvider != null) { - if (oldProvider.isDead()) { - Log.d(TAG, "replacing dead provider"); - removeProvider(oldProvider); - } else { - throw new IllegalArgumentException("Provider \"" + name + "\" already exists"); - } - } - - LocationProviderProxy proxy = new LocationProviderProxy(name, provider); - addProvider(proxy); - updateProvidersLocked(); - if (LocationManager.NETWORK_PROVIDER.equals(name)) { - mNetworkLocationProvider = proxy; - } - - // notify provider of current network state - proxy.updateNetworkState(mNetworkState, null); - } - } - - public void installGeocodeProvider(IGeocodeProvider provider) { - if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission"); - } - - mGeocodeProvider = provider; - } - private boolean isAllowedBySettingsLocked(String provider) { if (mEnabledProviders.contains(provider)) { return true; @@ -552,14 +545,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } // Use system settings ContentResolver resolver = mContext.getContentResolver(); - String allowedProviders = Settings.Secure.getString(resolver, - Settings.Secure.LOCATION_PROVIDERS_ALLOWED); - return ((allowedProviders != null) && (allowedProviders.contains(provider))); + return Settings.Secure.isLocationProviderEnabled(resolver, provider); } private void checkPermissionsSafe(String provider) { - if (LocationManager.GPS_PROVIDER.equals(provider) + if ((LocationManager.GPS_PROVIDER.equals(provider) + || LocationManager.PASSIVE_PROVIDER.equals(provider)) && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); @@ -575,7 +567,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } private boolean isAllowedProviderSafe(String provider) { - if (LocationManager.GPS_PROVIDER.equals(provider) + if ((LocationManager.GPS_PROVIDER.equals(provider) + || LocationManager.PASSIVE_PROVIDER.equals(provider)) && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { return false; @@ -599,18 +592,18 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } catch (SecurityException se) { throw se; } catch (Exception e) { - Log.e(TAG, "getAllProviders got exception:", e); + Slog.e(TAG, "getAllProviders got exception:", e); return null; } } private List<String> _getAllProvidersLocked() { if (LOCAL_LOGV) { - Log.v(TAG, "getAllProviders"); + Slog.v(TAG, "getAllProviders"); } ArrayList<String> out = new ArrayList<String>(mProviders.size()); for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderProxy p = mProviders.get(i); + LocationProviderInterface p = mProviders.get(i); out.add(p.getName()); } return out; @@ -624,18 +617,18 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } catch (SecurityException se) { throw se; } catch (Exception e) { - Log.e(TAG, "getProviders got exception:", e); + Slog.e(TAG, "getProviders got exception:", e); return null; } } private List<String> _getProvidersLocked(boolean enabledOnly) { if (LOCAL_LOGV) { - Log.v(TAG, "getProviders"); + Slog.v(TAG, "getProviders"); } ArrayList<String> out = new ArrayList<String>(mProviders.size()); for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderProxy p = mProviders.get(i); + LocationProviderInterface p = mProviders.get(i); String name = p.getName(); if (isAllowedProviderSafe(name)) { if (enabledOnly && !isAllowedBySettingsLocked(name)) { @@ -649,7 +642,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private void updateProvidersLocked() { for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderProxy p = mProviders.get(i); + LocationProviderInterface p = mProviders.get(i); boolean isEnabled = p.isEnabled(); String name = p.getName(); boolean shouldBeEnabled = isAllowedBySettingsLocked(name); @@ -666,7 +659,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private void updateProviderListenersLocked(String provider, boolean enabled) { int listeners = 0; - LocationProviderProxy p = mProvidersByName.get(provider); + LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { return; } @@ -772,7 +765,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance); pw.println(prefix + "mUid=" + mUid); pw.println(prefix + "mLastFixBroadcast:"); - mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " "); + if (mLastFixBroadcast != null) { + mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " "); + } pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast); } } @@ -789,7 +784,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run receiver.getListener().asBinder().linkToDeath(receiver, 0); } } catch (RemoteException e) { - Log.e(TAG, "linkToDeath failed:", e); + Slog.e(TAG, "linkToDeath failed:", e); return null; } } @@ -832,8 +827,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (SecurityException se) { throw se; + } catch (IllegalArgumentException iae) { + throw iae; } catch (Exception e) { - Log.e(TAG, "requestUpdates got exception:", e); + Slog.e(TAG, "requestUpdates got exception:", e); } } @@ -845,19 +842,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (SecurityException se) { throw se; + } catch (IllegalArgumentException iae) { + throw iae; } catch (Exception e) { - Log.e(TAG, "requestUpdates got exception:", e); + Slog.e(TAG, "requestUpdates got exception:", e); } } private void requestLocationUpdatesLocked(String provider, long minTime, float minDistance, Receiver receiver) { if (LOCAL_LOGV) { - Log.v(TAG, "_requestLocationUpdates: listener = " + receiver); + Slog.v(TAG, "_requestLocationUpdates: listener = " + receiver); } - LocationProviderProxy proxy = mProvidersByName.get(provider); - if (proxy == null) { + LocationProviderInterface p = mProvidersByName.get(provider); + if (p == null) { throw new IllegalArgumentException("provider=" + provider); } @@ -875,14 +874,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } if (newUid) { - proxy.addListener(callingUid); + p.addListener(callingUid); } boolean isProviderEnabled = isAllowedBySettingsLocked(provider); if (isProviderEnabled) { long minTimeForProvider = getMinTimeLocked(provider); - proxy.setMinTime(minTimeForProvider); - proxy.enableLocationTracking(true); + p.setMinTime(minTimeForProvider); + p.enableLocationTracking(true); } else { // Notify the listener that updates are currently disabled receiver.callProviderEnabledLocked(provider, false); @@ -899,8 +898,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (SecurityException se) { throw se; + } catch (IllegalArgumentException iae) { + throw iae; } catch (Exception e) { - Log.e(TAG, "removeUpdates got exception:", e); + Slog.e(TAG, "removeUpdates got exception:", e); } } @@ -911,14 +912,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (SecurityException se) { throw se; + } catch (IllegalArgumentException iae) { + throw iae; } catch (Exception e) { - Log.e(TAG, "removeUpdates got exception:", e); + Slog.e(TAG, "removeUpdates got exception:", e); } } private void removeUpdatesLocked(Receiver receiver) { if (LOCAL_LOGV) { - Log.v(TAG, "_removeUpdates: listener = " + receiver); + Slog.v(TAG, "_removeUpdates: listener = " + receiver); } // so wakelock calls will succeed @@ -942,9 +945,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Call dispose() on the obsolete update records. for (UpdateRecord record : oldRecords.values()) { if (!providerHasListener(record.mProvider, callingUid, receiver)) { - LocationProviderProxy proxy = mProvidersByName.get(record.mProvider); - if (proxy != null) { - proxy.removeListener(callingUid); + LocationProviderInterface p = mProvidersByName.get(record.mProvider); + if (p != null) { + p.removeListener(callingUid); } } record.disposeLocked(); @@ -968,7 +971,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run hasOtherListener = true; } - LocationProviderProxy p = mProvidersByName.get(provider); + LocationProviderInterface p = mProvidersByName.get(provider); if (p != null) { if (hasOtherListener) { p.setMinTime(getMinTimeLocked(provider)); @@ -994,7 +997,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run try { mGpsStatusProvider.addGpsStatusListener(listener); } catch (RemoteException e) { - Log.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e); + Slog.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e); return false; } return true; @@ -1005,7 +1008,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run try { mGpsStatusProvider.removeGpsStatusListener(listener); } catch (Exception e) { - Log.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e); + Slog.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e); } } } @@ -1025,12 +1028,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } synchronized (mLock) { - LocationProviderProxy proxy = mProvidersByName.get(provider); - if (proxy == null) { + LocationProviderInterface p = mProvidersByName.get(provider); + if (p == null) { return false; } - return proxy.sendExtraCommand(command, extras); + return p.sendExtraCommand(command, extras); } } @@ -1045,7 +1048,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } catch (RemoteException e) { - Log.e(TAG, "RemoteException in LocationManagerService.sendNiResponse"); + Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse"); return false; } } @@ -1140,7 +1143,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run alert.isInProximity(latitude, longitude, accuracy); if (!entered && inProximity) { if (LOCAL_LOGV) { - Log.v(TAG, "Entered alert"); + Slog.v(TAG, "Entered alert"); } mProximitiesEntered.add(alert); Intent enteredIntent = new Intent(); @@ -1156,7 +1159,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (PendingIntent.CanceledException e) { if (LOCAL_LOGV) { - Log.v(TAG, "Canceled proximity alert: " + alert, e); + Slog.v(TAG, "Canceled proximity alert: " + alert, e); } if (intentsToRemove == null) { intentsToRemove = new ArrayList<PendingIntent>(); @@ -1165,7 +1168,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } else if (entered && !inProximity) { if (LOCAL_LOGV) { - Log.v(TAG, "Exited alert"); + Slog.v(TAG, "Exited alert"); } mProximitiesEntered.remove(alert); Intent exitedIntent = new Intent(); @@ -1181,7 +1184,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (PendingIntent.CanceledException e) { if (LOCAL_LOGV) { - Log.v(TAG, "Canceled proximity alert: " + alert, e); + Slog.v(TAG, "Canceled proximity alert: " + alert, e); } if (intentsToRemove == null) { intentsToRemove = new ArrayList<PendingIntent>(); @@ -1192,7 +1195,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } else { // Mark alert for expiration if (LOCAL_LOGV) { - Log.v(TAG, "Expiring proximity alert: " + alert); + Slog.v(TAG, "Expiring proximity alert: " + alert); } if (intentsToRemove == null) { intentsToRemove = new ArrayList<PendingIntent>(); @@ -1204,8 +1207,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Remove expired alerts if (intentsToRemove != null) { for (PendingIntent i : intentsToRemove) { - ProximityAlert alert = mProximityAlerts.remove(i); + ProximityAlert alert = mProximityAlerts.get(i); mProximitiesEntered.remove(alert); + removeProximityAlertLocked(i); } } } @@ -1248,15 +1252,17 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (SecurityException se) { throw se; + } catch (IllegalArgumentException iae) { + throw iae; } catch (Exception e) { - Log.e(TAG, "addProximityAlert got exception:", e); + Slog.e(TAG, "addProximityAlert got exception:", e); } } private void addProximityAlertLocked(double latitude, double longitude, float radius, long expiration, PendingIntent intent) { if (LOCAL_LOGV) { - Log.v(TAG, "addProximityAlert: latitude = " + latitude + + Slog.v(TAG, "addProximityAlert: latitude = " + latitude + ", longitude = " + longitude + ", expiration = " + expiration + ", intent = " + intent); @@ -1280,7 +1286,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run mProximityReceiver = new Receiver(mProximityListener); for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderProxy provider = mProviders.get(i); + LocationProviderInterface provider = mProviders.get(i); requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver); } } @@ -1293,14 +1299,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (SecurityException se) { throw se; + } catch (IllegalArgumentException iae) { + throw iae; } catch (Exception e) { - Log.e(TAG, "removeProximityAlert got exception:", e); + Slog.e(TAG, "removeProximityAlert got exception:", e); } } private void removeProximityAlertLocked(PendingIntent intent) { if (LOCAL_LOGV) { - Log.v(TAG, "removeProximityAlert: intent = " + intent); + Slog.v(TAG, "removeProximityAlert: intent = " + intent); } mProximityAlerts.remove(intent); @@ -1312,7 +1320,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } /** - * @return null if the provider does not exits + * @return null if the provider does not exist * @throws SecurityException if the provider is not allowed to be * accessed by the caller */ @@ -1323,14 +1331,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (SecurityException se) { throw se; + } catch (IllegalArgumentException iae) { + throw iae; } catch (Exception e) { - Log.e(TAG, "_getProviderInfo got exception:", e); + Slog.e(TAG, "_getProviderInfo got exception:", e); return null; } } private Bundle _getProviderInfoLocked(String provider) { - LocationProviderProxy p = mProvidersByName.get(provider); + LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { return null; } @@ -1358,13 +1368,15 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (SecurityException se) { throw se; + } catch (IllegalArgumentException iae) { + throw iae; } catch (Exception e) { - Log.e(TAG, "isProviderEnabled got exception:", e); + Slog.e(TAG, "isProviderEnabled got exception:", e); return false; } } - public void reportLocation(Location location) { + public void reportLocation(Location location, boolean passive) { if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission"); @@ -1372,13 +1384,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location); Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location); + m.arg1 = (passive ? 1 : 0); mLocationHandler.sendMessageAtFrontOfQueue(m); } private boolean _isProviderEnabledLocked(String provider) { checkPermissionsSafe(provider); - LocationProviderProxy p = mProvidersByName.get(provider); + LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { throw new IllegalArgumentException("provider=" + provider); } @@ -1392,8 +1405,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } catch (SecurityException se) { throw se; + } catch (IllegalArgumentException iae) { + throw iae; } catch (Exception e) { - Log.e(TAG, "getLastKnownLocation got exception:", e); + Slog.e(TAG, "getLastKnownLocation got exception:", e); return null; } } @@ -1401,7 +1416,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private Location _getLastKnownLocationLocked(String provider) { checkPermissionsSafe(provider); - LocationProviderProxy p = mProvidersByName.get(provider); + LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { throw new IllegalArgumentException("provider=" + provider); } @@ -1436,14 +1451,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return true; } - private void handleLocationChangedLocked(Location location) { - String provider = location.getProvider(); + private void handleLocationChangedLocked(Location location, boolean passive) { + String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider()); ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); if (records == null || records.size() == 0) { return; } - LocationProviderProxy p = mProvidersByName.get(provider); + LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { return; } @@ -1480,7 +1495,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run lastLoc.set(location); } if (!receiver.callLocationChangedLocked(location)) { - Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver); + Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver); if (deadReceivers == null) { deadReceivers = new ArrayList<Receiver>(); } @@ -1494,7 +1509,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run r.mLastStatusBroadcast = newStatusUpdateTime; if (!receiver.callStatusChangedLocked(provider, status, extras)) { - Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver); + Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver); if (deadReceivers == null) { deadReceivers = new ArrayList<Receiver>(); } @@ -1523,23 +1538,26 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (mLock) { Location location = (Location) msg.obj; String provider = location.getProvider(); - - // notify other providers of the new location - for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderProxy proxy = mProviders.get(i); - if (!provider.equals(proxy.getName())) { - proxy.updateLocation(location); + boolean passive = (msg.arg1 == 1); + + if (!passive) { + // notify other providers of the new location + for (int i = mProviders.size() - 1; i >= 0; i--) { + LocationProviderInterface p = mProviders.get(i); + if (!provider.equals(p.getName())) { + p.updateLocation(location); + } } } if (isAllowedBySettingsLocked(provider)) { - handleLocationChangedLocked(location); + handleLocationChangedLocked(location, passive); } } } } catch (Exception e) { // Log, don't crash! - Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e); + Slog.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e); } } } @@ -1548,45 +1566,65 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - - if (action.equals(Intent.ACTION_PACKAGE_REMOVED) - || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) { + boolean queryRestart = action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART); + if (queryRestart + || action.equals(Intent.ACTION_PACKAGE_REMOVED) + || action.equals(Intent.ACTION_PACKAGE_RESTARTED) + || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { synchronized (mLock) { - 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); + int uidList[] = null; + if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { + uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); + } else { + uidList = new int[]{intent.getIntExtra(Intent.EXTRA_UID, -1)}; + } + if (uidList == null || uidList.length == 0) { + return; + } + for (int uid : uidList) { + 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 (queryRestart) { + setResultCode(Activity.RESULT_OK); + return; + } + 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); + ArrayList<ProximityAlert> removedAlerts = null; + for (ProximityAlert i : mProximityAlerts.values()) { + if (i.mUid == uid) { + if (queryRestart) { + setResultCode(Activity.RESULT_OK); + return; + } + 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 (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); + if (removedAlerts != null) { + for (int i=removedAlerts.size()-1; i>=0; i--) { + removeProximityAlertLocked(removedAlerts.get(i).mIntent); + } } } } @@ -1600,13 +1638,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE; } NetworkInfo info = - (NetworkInfo)intent.getExtra(ConnectivityManager.EXTRA_NETWORK_INFO); + (NetworkInfo)intent.getExtra(ConnectivityManager.EXTRA_NETWORK_INFO); // Notify location providers of current network state synchronized (mLock) { for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderProxy provider = mProviders.get(i); - if (provider.requiresNetwork()) { + LocationProviderInterface provider = mProviders.get(i); + if (provider.isEnabled() && provider.requiresNetwork()) { provider.updateNetworkState(mNetworkState, info); } } @@ -1626,7 +1664,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } catch (Exception e) { // This is to catch a runtime exception thrown when we try to release an // already released lock. - Log.e(TAG, "exception in acquireWakeLock()", e); + Slog.e(TAG, "exception in acquireWakeLock()", e); } } } @@ -1646,7 +1684,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } catch (Exception e) { // This is to catch a runtime exception thrown when we try to release an // already released lock. - Log.e(TAG, "exception in releaseWakeLock()", e); + Slog.e(TAG, "exception in releaseWakeLock()", e); } } } @@ -1655,15 +1693,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Geocoder public String getFromLocation(double latitude, double longitude, int maxResults, - String language, String country, String variant, String appName, List<Address> addrs) { + GeocoderParams params, List<Address> addrs) { if (mGeocodeProvider != null) { - try { - return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country, - variant, appName, addrs); - } catch (RemoteException e) { - Log.e(TAG, "getFromLocation failed", e); - mGeocodeProvider = null; - } + return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, + params, addrs); } return null; } @@ -1672,17 +1705,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run public String getFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude, int maxResults, - String language, String country, String variant, String appName, List<Address> addrs) { + GeocoderParams params, List<Address> addrs) { if (mGeocodeProvider != null) { - try { - return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude, - lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, language, country, variant, appName, addrs); - } catch (RemoteException e) { - Log.e(TAG, "getFromLocationName failed", e); - mGeocodeProvider = null; - } + return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude, + lowerLeftLongitude, upperRightLatitude, upperRightLongitude, + maxResults, params, addrs); } return null; } @@ -1707,6 +1735,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { checkMockPermissionsSafe(); + if (LocationManager.PASSIVE_PROVIDER.equals(name)) { + throw new IllegalArgumentException("Cannot mock the passive location provider"); + } + long identity = Binder.clearCallingIdentity(); synchronized (mLock) { MockProvider provider = new MockProvider(name, this, @@ -1716,16 +1748,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // remove the real provider if we are replacing GPS or network provider if (LocationManager.GPS_PROVIDER.equals(name) || LocationManager.NETWORK_PROVIDER.equals(name)) { - LocationProviderProxy proxy = mProvidersByName.get(name); - if (proxy != null) { - proxy.enableLocationTracking(false); - removeProvider(proxy); + LocationProviderInterface p = mProvidersByName.get(name); + if (p != null) { + p.enableLocationTracking(false); + removeProvider(p); } } if (mProvidersByName.get(name) != null) { throw new IllegalArgumentException("Provider \"" + name + "\" already exists"); } - addProvider(new LocationProviderProxy(name, provider)); + addProvider(provider); mMockProviders.put(name, provider); mLastKnownLocation.put(name, null); updateProvidersLocked(); @@ -1843,7 +1875,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private void log(String log) { if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, log); + Slog.d(TAG, log); } } @@ -1924,6 +1956,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run i.getValue().dump(pw, " "); } } + for (LocationProviderInterface provider: mProviders) { + String state = provider.getInternalState(); + if (state != null) { + pw.println(provider.getName() + " Internal State:"); + pw.write(state); + } + } } } } diff --git a/services/java/com/android/server/MasterClearReceiver.java b/services/java/com/android/server/MasterClearReceiver.java index 3c366da..27a8a74 100644 --- a/services/java/com/android/server/MasterClearReceiver.java +++ b/services/java/com/android/server/MasterClearReceiver.java @@ -16,38 +16,33 @@ package com.android.server; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.BroadcastReceiver; -import android.os.RemoteException; -import android.os.ICheckinService; -import android.os.ServiceManager; +import android.os.RecoverySystem; import android.util.Log; +import android.util.Slog; -public class MasterClearReceiver extends BroadcastReceiver { +import java.io.IOException; +public class MasterClearReceiver extends BroadcastReceiver { private static final String TAG = "MasterClear"; @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) { - if (!intent.getBooleanExtra("android.intent.extra.from_trusted_server", false)) { - Log.w(TAG, "Ignoring master clear request -- not from trusted server."); + if (!"google.com".equals(intent.getStringExtra("from"))) { + Slog.w(TAG, "Ignoring master clear request -- not from trusted server."); return; } } - Log.w(TAG, "!!! FACTORY RESETTING DEVICE !!!"); - ICheckinService service = - ICheckinService.Stub.asInterface( - ServiceManager.getService("checkin")); - if (service != null) { - try { - // This RPC should never return. - service.masterClear(); - } catch (RemoteException e) { - Log.w("MasterClear", - "Unable to invoke ICheckinService.masterClear()"); - } + + try { + Slog.w(TAG, "!!! FACTORY RESET !!!"); + RecoverySystem.rebootWipeUserData(context); + Log.wtf(TAG, "Still running after master clear?!"); + } catch (IOException e) { + Slog.e(TAG, "Can't perform master clear/factory reset", e); } } } diff --git a/services/java/com/android/server/MountListener.java b/services/java/com/android/server/MountListener.java deleted file mode 100644 index 3e53585..0000000 --- a/services/java/com/android/server/MountListener.java +++ /dev/null @@ -1,325 +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.net.LocalSocketAddress; -import android.net.LocalSocket; -import android.os.Environment; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.util.Config; -import android.util.Log; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.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. - */ -final class MountListener implements Runnable { - - private static final String TAG = "MountListener"; - - // ** THE FOLLOWING STRING CONSTANTS MUST MATCH VALUES IN system/vold/ - - // socket name for connecting to vold - private static final String VOLD_SOCKET = "vold"; - - // 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:"; - - // 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 vol service daemon - */ - private MountService mService; - - /** - * Stream for sending commands to the vol service daemon. - */ - private OutputStream mOutputStream; - - /** - * Cached value indicating whether or not USB mass storage is enabled. - */ - private boolean mUmsEnabled; - - /** - * Cached value indicating whether or not USB mass storage is connected. - */ - private boolean mUmsConnected; - - /** - * Constructor for MountListener - * - * @param service The MountListener we are handling communication with USB - * daemon for. - */ - MountListener(MountService service) { - mService = service; - } - - /** - * Process and dispatches events received from the vol 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); - - int colonIndex = event.indexOf(':'); - String path = (colonIndex > 0 ? event.substring(colonIndex + 1) : null); - - if (event.equals(VOLD_EVT_UMS_ENABLED)) { - mUmsEnabled = true; - } else if (event.equals(VOLD_EVT_UMS_DISABLED)) { - mUmsEnabled = false; - } else if (event.equals(VOLD_EVT_UMS_CONNECTED)) { - mUmsConnected = true; - mService.notifyUmsConnected(); - } else if (event.equals(VOLD_EVT_UMS_DISCONNECTED)) { - mUmsConnected = false; - mService.notifyUmsDisconnected(); - } else if (event.startsWith(VOLD_EVT_NOMEDIA)) { - mService.notifyMediaRemoved(path); - } else if (event.startsWith(VOLD_EVT_UNMOUNTED)) { - mService.notifyMediaUnmounted(path); - } 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(VOLD_EVT_MOUNTED_RO)) { - mService.notifyMediaMounted(path, true); - } else if (event.startsWith(VOLD_EVT_UMS)) { - mService.notifyMediaShared(path); - } 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(VOLD_EVT_DAMAGED)) { - mService.notifyMediaUnmountable(path); - } else if (event.startsWith(VOLD_EVT_EJECTING)) { - mService.notifyMediaEject(path); - } - } - - /** - * Sends a command to the mount service daemon via a local socket - * - * @param command The command to send to the mount service daemon - */ - private void writeCommand(String command) { - writeCommand2(command, null); - } - - /** - * Sends a command to the mount service daemon via a local socket - * with a single argument - * - * @param command The command to send to the mount service daemon - * @param argument The argument to send with the command (or null) - */ - private void writeCommand2(String command, String argument) { - synchronized (this) { - if (mOutputStream == null) { - Log.e(TAG, "No connection to vold", new IllegalStateException()); - } else { - StringBuilder builder = new StringBuilder(command); - if (argument != null) { - builder.append(argument); - } - builder.append('\0'); - - try { - mOutputStream.write(builder.toString().getBytes()); - } catch (IOException ex) { - Log.e(TAG, "IOException in writeCommand", ex); - } - } - } - } - - /** - * Opens a socket to communicate with the mount service daemon and listens - * for events from the daemon. - * - */ - private void listenToSocket() { - LocalSocket socket = null; - - try { - socket = new LocalSocket(); - LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET, - LocalSocketAddress.Namespace.RESERVED); - - socket.connect(address); - - InputStream inputStream = socket.getInputStream(); - mOutputStream = socket.getOutputStream(); - - byte[] buffer = new byte[100]; - - writeCommand(VOLD_CMD_SEND_UMS_STATUS); - mountMedia(Environment.getExternalStorageDirectory().getAbsolutePath()); - - while (true) { - int count = inputStream.read(buffer); - if (count < 0) break; - - int start = 0; - for (int i = 0; i < count; i++) { - if (buffer[i] == 0) { - String event = new String(buffer, start, i - start); - handleEvent(event); - start = i + 1; - } - } - } - } catch (IOException ex) { - // This exception is normal when running in desktop simulator - // where there is no mount daemon to talk to - - // log("IOException in listenToSocket"); - } - - synchronized (this) { - if (mOutputStream != null) { - try { - mOutputStream.close(); - } catch (IOException e) { - Log.w(TAG, "IOException closing output stream"); - } - - mOutputStream = null; - } - } - - try { - if (socket != null) { - socket.close(); - } - } catch (IOException ex) { - Log.w(TAG, "IOException closing socket"); - } - - /* - * Sleep before trying again. - * This should not happen except while debugging. - * Without this sleep, the emulator will spin and - * create tons of throwaway LocalSockets, making - * system_server GC constantly. - */ - Log.e(TAG, "Failed to connect to vold", new IllegalStateException()); - SystemClock.sleep(2000); - } - - /** - * Main loop for MountListener thread. - */ - public void run() { - // ugly hack for the simulator. - if ("simulator".equals(SystemProperties.get("ro.product.device"))) { - SystemProperties.set("EXTERNAL_STORAGE_STATE", Environment.MEDIA_MOUNTED); - // usbd does not run in the simulator, so send a fake device mounted event to trigger the Media Scanner - mService.notifyMediaMounted(Environment.getExternalStorageDirectory().getPath(), false); - - // no usbd in the simulator, so no point in hanging around. - return; - } - - try { - while (true) { - listenToSocket(); - } - } catch (Throwable t) { - // catch all Throwables so we don't bring down the system process - Log.e(TAG, "Fatal error " + t + " in MountListener thread!"); - } - } - - /** - * @return true if USB mass storage is enabled - */ - boolean getMassStorageEnabled() { - return mUmsEnabled; - } - - /** - * Enables or disables USB mass storage support. - * - * @param enable true to enable USB mass storage support - */ - void setMassStorageEnabled(boolean enable) { - writeCommand(enable ? VOLD_CMD_ENABLE_UMS : VOLD_CMD_DISABLE_UMS); - } - - /** - * @return true if USB mass storage is connected - */ - boolean getMassStorageConnected() { - return mUmsConnected; - } - - /** - * Mount media at given mount point. - */ - public void mountMedia(String mountPoint) { - writeCommand2(VOLD_CMD_MOUNT_VOLUME, mountPoint); - } - - /** - * Unmount media at given mount point. - */ - public void ejectMedia(String 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 ddf7c56..6ceeb95 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -16,630 +16,1295 @@ package com.android.server; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; +import com.android.server.am.ActivityManagerService; + 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.storage.IMountService; +import android.os.storage.IMountServiceListener; +import android.os.storage.IMountShutdownObserver; +import android.os.storage.StorageResultCode; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; import android.os.RemoteException; +import android.os.IBinder; +import android.os.Environment; +import android.os.ServiceManager; +import android.os.SystemClock; import android.os.SystemProperties; -import android.os.UEventObserver; -import android.text.TextUtils; -import android.util.Log; - -import java.io.File; -import java.io.FileReader; +import android.util.Slog; +import java.util.ArrayList; +import java.util.HashSet; /** - * MountService implements an to the mount service daemon - * @hide + * MountService implements back-end services for platform storage + * management. + * @hide - Applications should use android.os.storage.StorageManager + * to access the MountService. */ -class MountService extends IMountService.Stub { +class MountService extends IMountService.Stub + implements INativeDaemonConnectorCallbacks { + private static final boolean LOCAL_LOGD = false; + private static final boolean DEBUG_UNMOUNT = false; + private static final boolean DEBUG_EVENTS = false; private static final String TAG = "MountService"; - /** - * Binder context for this service - */ - private Context mContext; - - /** - * listener object for communicating with the mount service daemon + /* + * Internal vold volume state constants */ - private MountListener mListener; + class VolumeState { + public static final int Init = -1; + public static final int NoMedia = 0; + public static final int Idle = 1; + public static final int Pending = 2; + public static final int Checking = 3; + public static final int Mounted = 4; + public static final int Unmounting = 5; + public static final int Formatting = 6; + public static final int Shared = 7; + public static final int SharedMnt = 8; + } - /** - * The notification that is shown when a USB mass storage host - * is connected. - * <p> - * This is lazily created, so use {@link #setUsbStorageNotification()}. + /* + * Internal vold response code constants */ - private Notification mUsbStorageNotification; + class VoldResponseCode { + /* + * 100 series - Requestion action was initiated; expect another reply + * before proceeding with a new command. + */ + public static final int VolumeListResult = 110; + public static final int AsecListResult = 111; + public static final int StorageUsersListResult = 112; + + /* + * 200 series - Requestion action has been successfully completed. + */ + public static final int ShareStatusResult = 210; + public static final int AsecPathResult = 211; + public static final int ShareEnabledResult = 212; + + /* + * 400 series - Command was accepted, but the requested action + * did not take place. + */ + public static final int OpFailedNoMedia = 401; + public static final int OpFailedMediaBlank = 402; + public static final int OpFailedMediaCorrupt = 403; + public static final int OpFailedVolNotMounted = 404; + public static final int OpFailedStorageBusy = 405; + public static final int OpFailedStorageNotFound = 406; + + /* + * 600 series - Unsolicited broadcasts. + */ + public static final int VolumeStateChange = 605; + public static final int ShareAvailabilityChange = 620; + public static final int VolumeDiskInserted = 630; + public static final int VolumeDiskRemoved = 631; + public static final int VolumeBadRemoval = 632; + } + private Context mContext; + private NativeDaemonConnector mConnector; + private String mLegacyState = Environment.MEDIA_REMOVED; + private PackageManagerService mPms; + private boolean mUmsEnabling; + // Used as a lock for methods that register/unregister listeners. + final private ArrayList<MountServiceBinderListener> mListeners = + new ArrayList<MountServiceBinderListener>(); + private boolean mBooted = false; + private boolean mReady = false; + private boolean mSendUmsConnectedOnBoot = false; /** - * 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 hash of currently mounted secure containers. + * Used as a lock in methods to manipulate secure containers. */ - private Notification mMediaStorageNotification; - - private boolean mShowSafeUnmountNotificationWhenUnmounted; + final private HashSet<String> mAsecMountSet = new HashSet<String>(); + + private static final int H_UNMOUNT_PM_UPDATE = 1; + private static final int H_UNMOUNT_PM_DONE = 2; + private static final int H_UNMOUNT_MS = 3; + private static final int RETRY_UNMOUNT_DELAY = 30; // in ms + private static final int MAX_UNMOUNT_RETRIES = 4; + + class UnmountCallBack { + String path; + int retries; + boolean force; + + UnmountCallBack(String path, boolean force) { + retries = 0; + this.path = path; + this.force = force; + } - private boolean mPlaySounds; + void handleFinished() { + if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path); + doUnmountVolume(path, true); + } + } - private boolean mMounted; + class UmsEnableCallBack extends UnmountCallBack { + String method; - private boolean mAutoStartUms; + UmsEnableCallBack(String path, String method, boolean force) { + super(path, force); + this.method = method; + } - /** - * Constructs a new MountService instance - * - * @param context Binder context for this service - */ - public MountService(Context context) { - mContext = context; + @Override + void handleFinished() { + super.handleFinished(); + doShareUnshareVolume(path, method, true); + } + } - // 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); + class ShutdownCallBack extends UnmountCallBack { + IMountShutdownObserver observer; + ShutdownCallBack(String path, IMountShutdownObserver observer) { + super(path, true); + this.observer = observer; + } - mListener = new MountListener(this); - mShowSafeUnmountNotificationWhenUnmounted = false; + @Override + void handleFinished() { + int ret = doUnmountVolume(path, true); + if (observer != null) { + try { + observer.onShutDownComplete(ret); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException when shutting down"); + } + } + } + } - mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1"); + class MountServiceHandler extends Handler { + ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>(); + boolean mUpdatingStatus = false; - mAutoStartUms = SystemProperties.get("persist.service.mount.umsauto", "0").equals("1"); - } + MountServiceHandler(Looper l) { + super(l); + } - BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + public void handleMessage(Message msg) { + switch (msg.what) { + case H_UNMOUNT_PM_UPDATE: { + if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE"); + UnmountCallBack ucb = (UnmountCallBack) msg.obj; + mForceUnmounts.add(ucb); + if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus); + // Register only if needed. + if (!mUpdatingStatus) { + if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager"); + mUpdatingStatus = true; + mPms.updateExternalMediaStatus(false, true); + } + break; + } + case H_UNMOUNT_PM_DONE: { + if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE"); + if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests"); + mUpdatingStatus = false; + int size = mForceUnmounts.size(); + int sizeArr[] = new int[size]; + int sizeArrN = 0; + // Kill processes holding references first + ActivityManagerService ams = (ActivityManagerService) + ServiceManager.getService("activity"); + for (int i = 0; i < size; i++) { + UnmountCallBack ucb = mForceUnmounts.get(i); + String path = ucb.path; + boolean done = false; + if (!ucb.force) { + done = true; + } else { + int pids[] = getStorageUsers(path); + if (pids == null || pids.length == 0) { + done = true; + } else { + // Eliminate system process here? + ams.killPids(pids, "unmount media"); + // Confirm if file references have been freed. + pids = getStorageUsers(path); + if (pids == null || pids.length == 0) { + done = true; + } + } + } + if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) { + // Retry again + Slog.i(TAG, "Retrying to kill storage users again"); + mHandler.sendMessageDelayed( + mHandler.obtainMessage(H_UNMOUNT_PM_DONE, + ucb.retries++), + RETRY_UNMOUNT_DELAY); + } else { + if (ucb.retries >= MAX_UNMOUNT_RETRIES) { + Slog.i(TAG, "Failed to unmount media inspite of " + + MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now"); + } + sizeArr[sizeArrN++] = i; + mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS, + ucb)); + } + } + // Remove already processed elements from list. + for (int i = (sizeArrN-1); i >= 0; i--) { + mForceUnmounts.remove(sizeArr[i]); + } + break; + } + case H_UNMOUNT_MS : { + if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS"); + UnmountCallBack ucb = (UnmountCallBack) msg.obj; + ucb.handleFinished(); + break; + } + } + } + }; + final private HandlerThread mHandlerThread; + final private Handler mHandler; + + private void waitForReady() { + while (mReady == false) { + for (int retries = 5; retries > 0; retries--) { + if (mReady) { + return; + } + SystemClock.sleep(1000); + } + Slog.w(TAG, "Waiting too long for mReady!"); + } + } + + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { - Thread thread = new Thread(mListener, MountListener.class.getName()); - thread.start(); + mBooted = true; + + /* + * In the simulator, we need to broadcast a volume mounted event + * to make the media scanner run. + */ + if ("simulator".equals(SystemProperties.get("ro.product.device"))) { + notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia, VolumeState.Mounted); + return; + } + new Thread() { + public void run() { + try { + String path = Environment.getExternalStorageDirectory().getPath(); + String state = getVolumeState(path); + + if (state.equals(Environment.MEDIA_UNMOUNTED)) { + int rc = doMountVolume(path); + if (rc != StorageResultCode.OperationSucceeded) { + Slog.e(TAG, String.format("Boot-time mount failed (%d)", rc)); + } + } else if (state.equals(Environment.MEDIA_SHARED)) { + /* + * Bootstrap UMS enabled state since vold indicates + * the volume is shared (runtime restart while ums enabled) + */ + notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Shared); + } + + /* + * If UMS was connected on boot, send the connected event + * now that we're up. + */ + if (mSendUmsConnectedOnBoot) { + sendUmsIntent(true); + mSendUmsConnectedOnBoot = false; + } + } catch (Exception ex) { + Slog.e(TAG, "Boot-time mount exception", ex); + } + } + }.start(); } } }; - public void shutdown() { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.SHUTDOWN) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires SHUTDOWN permission"); + private final class MountServiceBinderListener implements IBinder.DeathRecipient { + final IMountServiceListener mListener; + + MountServiceBinderListener(IMountServiceListener listener) { + mListener = listener; + } - Log.d(TAG, "Shutting down"); - String state = Environment.getExternalStorageState(); + public void binderDied() { + if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!"); + synchronized(mListeners) { + mListeners.remove(this); + mListener.asBinder().unlinkToDeath(this, 0); + } + } + } - if (state.equals(Environment.MEDIA_SHARED)) { - /* - * If the media is currently shared, unshare it. - * XXX: This is still dangerous!. We should not - * be rebooting at *all* if UMS is enabled, since - * the UMS host could have dirty FAT cache entries - * yet to flush. - */ - try { - setMassStorageEnabled(false); - } catch (Exception e) { - Log.e(TAG, "ums disable failed", e); + private void doShareUnshareVolume(String path, String method, boolean enable) { + // TODO: Add support for multiple share methods + if (!method.equals("ums")) { + throw new IllegalArgumentException(String.format("Method %s not supported", method)); + } + + try { + mConnector.doCommand(String.format( + "volume %sshare %s %s", (enable ? "" : "un"), path, method)); + } catch (NativeDaemonConnectorException e) { + Slog.e(TAG, "Failed to share/unshare", e); + } + } + + private void updatePublicVolumeState(String path, String state) { + if (!path.equals(Environment.getExternalStorageDirectory().getPath())) { + Slog.w(TAG, "Multiple volumes not currently supported"); + return; + } + + if (mLegacyState.equals(state)) { + Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state)); + return; + } + // Update state on PackageManager + if (Environment.MEDIA_UNMOUNTED.equals(state)) { + mPms.updateExternalMediaStatus(false, false); + } else if (Environment.MEDIA_MOUNTED.equals(state)) { + mPms.updateExternalMediaStatus(true, false); + } + String oldState = mLegacyState; + mLegacyState = state; + + synchronized (mListeners) { + for (int i = mListeners.size() -1; i >= 0; i--) { + MountServiceBinderListener bl = mListeners.get(i); + try { + bl.mListener.onStorageStateChanged(path, oldState, state); + } catch (RemoteException rex) { + Slog.e(TAG, "Listener dead"); + mListeners.remove(i); + } catch (Exception ex) { + Slog.e(TAG, "Listener failed", ex); + } } - } else if (state.equals(Environment.MEDIA_CHECKING)) { - /* - * If the media is being checked, then we need to wait for - * it to complete before being able to proceed. - */ - // XXX: @hackbod - Should we disable the ANR timer here? - int retries = 30; - while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) { + } + } + + /** + * + * Callback from NativeDaemonConnector + */ + public void onDaemonConnected() { + /* + * Since we'll be calling back into the NativeDaemonConnector, + * we need to do our work in a new thread. + */ + new Thread() { + public void run() { + /** + * Determine media state and UMS detection status + */ + String path = Environment.getExternalStorageDirectory().getPath(); + String state = Environment.MEDIA_REMOVED; + try { - Thread.sleep(1000); - } catch (InterruptedException iex) { - Log.e(TAG, "Interrupted while waiting for media", iex); - break; + String[] vols = mConnector.doListCommand( + "volume list", VoldResponseCode.VolumeListResult); + for (String volstr : vols) { + String[] tok = volstr.split(" "); + // FMT: <label> <mountpoint> <state> + if (!tok[1].equals(path)) { + Slog.w(TAG, String.format( + "Skipping unknown volume '%s'",tok[1])); + continue; + } + int st = Integer.parseInt(tok[2]); + if (st == VolumeState.NoMedia) { + state = Environment.MEDIA_REMOVED; + } else if (st == VolumeState.Idle) { + state = Environment.MEDIA_UNMOUNTED; + } else if (st == VolumeState.Mounted) { + state = Environment.MEDIA_MOUNTED; + Slog.i(TAG, "Media already mounted on daemon connection"); + } else if (st == VolumeState.Shared) { + state = Environment.MEDIA_SHARED; + Slog.i(TAG, "Media shared on daemon connection"); + } else { + throw new Exception(String.format("Unexpected state %d", st)); + } + } + if (state != null) { + if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state); + updatePublicVolumeState(path, state); + } + } catch (Exception e) { + Slog.e(TAG, "Error processing initial volume state", e); + updatePublicVolumeState(path, Environment.MEDIA_REMOVED); } - state = Environment.getExternalStorageState(); + + try { + boolean avail = doGetShareMethodAvailable("ums"); + notifyShareAvailabilityChange("ums", avail); + } catch (Exception ex) { + Slog.w(TAG, "Failed to get share availability"); + } + /* + * Now that we've done our initialization, release + * the hounds! + */ + mReady = true; } - if (retries == 0) { - Log.e(TAG, "Timed out waiting for media to check"); + }.start(); + } + + /** + * Callback from NativeDaemonConnector + */ + public boolean onEvent(int code, String raw, String[] cooked) { + Intent in = null; + + if (DEBUG_EVENTS) { + StringBuilder builder = new StringBuilder(); + builder.append("onEvent::"); + builder.append(" raw= " + raw); + if (cooked != null) { + builder.append(" cooked = " ); + for (String str : cooked) { + builder.append(" " + str); + } } + Slog.i(TAG, builder.toString()); } - - if (state.equals(Environment.MEDIA_MOUNTED)) { + if (code == VoldResponseCode.VolumeStateChange) { /* - * If the media is mounted, then gracefully unmount it. + * One of the volumes we're managing has changed state. + * Format: "NNN Volume <label> <path> state changed + * from <old_#> (<old_str>) to <new_#> (<new_str>)" */ + notifyVolumeStateChange( + cooked[2], cooked[3], Integer.parseInt(cooked[7]), + Integer.parseInt(cooked[10])); + } else if (code == VoldResponseCode.ShareAvailabilityChange) { + // FMT: NNN Share method <method> now <available|unavailable> + boolean avail = false; + if (cooked[5].equals("available")) { + avail = true; + } + notifyShareAvailabilityChange(cooked[3], avail); + } else if ((code == VoldResponseCode.VolumeDiskInserted) || + (code == VoldResponseCode.VolumeDiskRemoved) || + (code == VoldResponseCode.VolumeBadRemoval)) { + // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>) + // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>) + // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>) + final String label = cooked[2]; + final String path = cooked[3]; + int major = -1; + int minor = -1; + try { - String m = Environment.getExternalStorageDirectory().toString(); - unmountMedia(m); + String devComp = cooked[6].substring(1, cooked[6].length() -1); + String[] devTok = devComp.split(":"); + major = Integer.parseInt(devTok[0]); + minor = Integer.parseInt(devTok[1]); + } catch (Exception ex) { + Slog.e(TAG, "Failed to parse major/minor", ex); + } - int retries = 12; - while (!state.equals(Environment.MEDIA_UNMOUNTED) && (retries-- >=0)) { - try { - Thread.sleep(1000); - } catch (InterruptedException iex) { - Log.e(TAG, "Interrupted while waiting for media", iex); - break; + if (code == VoldResponseCode.VolumeDiskInserted) { + new Thread() { + public void run() { + try { + int rc; + if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) { + Slog.w(TAG, String.format("Insertion mount failed (%d)", rc)); + } + } catch (Exception ex) { + Slog.w(TAG, "Failed to mount media on insertion", ex); + } } - state = Environment.getExternalStorageState(); + }.start(); + } else if (code == VoldResponseCode.VolumeDiskRemoved) { + /* + * This event gets trumped if we're already in BAD_REMOVAL state + */ + if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) { + return true; } - if (retries == 0) { - Log.e(TAG, "Timed out waiting for media to unmount"); - } - } catch (Exception e) { - Log.e(TAG, "external storage unmount failed", e); + /* Send the media unmounted event first */ + if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first"); + updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); + in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); + mContext.sendBroadcast(in); + + if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed"); + updatePublicVolumeState(path, Environment.MEDIA_REMOVED); + in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path)); + } else if (code == VoldResponseCode.VolumeBadRemoval) { + if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first"); + /* Send the media unmounted event first */ + updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); + in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); + mContext.sendBroadcast(in); + + if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal"); + updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL); + in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path)); + } else { + Slog.e(TAG, String.format("Unknown code {%d}", code)); } + } else { + return false; + } + + if (in != null) { + mContext.sendBroadcast(in); } + return true; } - /** - * @return true if USB mass storage support is enabled. - */ - public boolean getMassStorageEnabled() throws RemoteException { - return mListener.getMassStorageEnabled(); + private void notifyVolumeStateChange(String label, String path, int oldState, int newState) { + String vs = getVolumeState(path); + if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs); + + Intent in = null; + + if (oldState == VolumeState.Shared && newState != oldState) { + if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent"); + mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED, + Uri.parse("file://" + path))); + } + + if (newState == VolumeState.Init) { + } else if (newState == VolumeState.NoMedia) { + // NoMedia is handled via Disk Remove events + } else if (newState == VolumeState.Idle) { + /* + * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or + * if we're in the process of enabling UMS + */ + if (!vs.equals( + Environment.MEDIA_BAD_REMOVAL) && !vs.equals( + Environment.MEDIA_NOFS) && !vs.equals( + Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) { + if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable"); + updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); + in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); + } + } else if (newState == VolumeState.Pending) { + } else if (newState == VolumeState.Checking) { + if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking"); + updatePublicVolumeState(path, Environment.MEDIA_CHECKING); + in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path)); + } else if (newState == VolumeState.Mounted) { + if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted"); + updatePublicVolumeState(path, Environment.MEDIA_MOUNTED); + in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path)); + in.putExtra("read-only", false); + } else if (newState == VolumeState.Unmounting) { + in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path)); + } else if (newState == VolumeState.Formatting) { + } else if (newState == VolumeState.Shared) { + if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted"); + /* Send the media unmounted event first */ + updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); + in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); + mContext.sendBroadcast(in); + + if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared"); + updatePublicVolumeState(path, Environment.MEDIA_SHARED); + in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path)); + if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent"); + } else if (newState == VolumeState.SharedMnt) { + Slog.e(TAG, "Live shared mounts not supported yet!"); + return; + } else { + Slog.e(TAG, "Unhandled VolumeState {" + newState + "}"); + } + + if (in != null) { + mContext.sendBroadcast(in); + } } - /** - * Enables or disables USB mass storage support. - * - * @param enable true to enable USB mass storage support - */ - public void setMassStorageEnabled(boolean enable) throws RemoteException { - mListener.setMassStorageEnabled(enable); + private boolean doGetShareMethodAvailable(String method) { + ArrayList<String> rsp = mConnector.doCommand("share status " + method); + + for (String line : rsp) { + String []tok = line.split(" "); + int code; + try { + code = Integer.parseInt(tok[0]); + } catch (NumberFormatException nfe) { + Slog.e(TAG, String.format("Error parsing code %s", tok[0])); + return false; + } + if (code == VoldResponseCode.ShareStatusResult) { + if (tok[2].equals("available")) + return true; + return false; + } else { + Slog.e(TAG, String.format("Unexpected response code %d", code)); + return false; + } + } + Slog.e(TAG, "Got an empty response"); + return false; } - /** - * @return true if USB mass storage is connected. - */ - public boolean getMassStorageConnected() throws RemoteException { - return mListener.getMassStorageConnected(); + private int doMountVolume(String path) { + int rc = StorageResultCode.OperationSucceeded; + + if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path); + try { + mConnector.doCommand(String.format("volume mount %s", path)); + } catch (NativeDaemonConnectorException e) { + /* + * Mount failed for some reason + */ + Intent in = null; + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedNoMedia) { + /* + * Attempt to mount but no media inserted + */ + rc = StorageResultCode.OperationFailedNoMedia; + } else if (code == VoldResponseCode.OpFailedMediaBlank) { + if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs"); + /* + * Media is blank or does not contain a supported filesystem + */ + updatePublicVolumeState(path, Environment.MEDIA_NOFS); + in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path)); + rc = StorageResultCode.OperationFailedMediaBlank; + } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { + if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt"); + /* + * Volume consistency check failed + */ + updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); + in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path)); + rc = StorageResultCode.OperationFailedMediaCorrupt; + } else { + rc = StorageResultCode.OperationFailedInternalError; + } + + /* + * Send broadcast intent (if required for the failure) + */ + if (in != null) { + mContext.sendBroadcast(in); + } + } + + return rc; } - - /** - * Attempt to mount external media + + /* + * If force is not set, we do not unmount if there are + * processes holding references to the volume about to be unmounted. + * If force is set, all the processes holding references need to be + * killed via the ActivityManager before actually unmounting the volume. + * This might even take a while and might be retried after timed delays + * to make sure we dont end up in an instable state and kill some core + * processes. */ - public void mountMedia(String mountPath) throws RemoteException { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); + private int doUnmountVolume(String path, boolean force) { + if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) { + return VoldResponseCode.OpFailedVolNotMounted; + } + // Redundant probably. But no harm in updating state again. + mPms.updateExternalMediaStatus(false, false); + try { + mConnector.doCommand(String.format( + "volume unmount %s%s", path, (force ? " force" : ""))); + // We unmounted the volume. None of the asec containers are available now. + synchronized (mAsecMountSet) { + mAsecMountSet.clear(); + } + return StorageResultCode.OperationSucceeded; + } catch (NativeDaemonConnectorException e) { + // Don't worry about mismatch in PackageManager since the + // call back will handle the status changes any way. + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedVolNotMounted) { + return StorageResultCode.OperationFailedStorageNotMounted; + } else if (code == VoldResponseCode.OpFailedStorageBusy) { + return StorageResultCode.OperationFailedStorageBusy; + } else { + return StorageResultCode.OperationFailedInternalError; + } } - mListener.mountMedia(mountPath); } - /** - * Attempt to unmount external media to prepare for eject - */ - public void unmountMedia(String mountPath) throws RemoteException { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); + private int doFormatVolume(String path) { + try { + String cmd = String.format("volume format %s", path); + mConnector.doCommand(cmd); + return StorageResultCode.OperationSucceeded; + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedNoMedia) { + return StorageResultCode.OperationFailedNoMedia; + } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { + return StorageResultCode.OperationFailedMediaCorrupt; + } else { + return StorageResultCode.OperationFailedInternalError; + } } + } - // Set a flag so that when we get the unmounted event, we know - // to display the notification - mShowSafeUnmountNotificationWhenUnmounted = true; + private boolean doGetVolumeShared(String path, String method) { + String cmd = String.format("volume shared %s %s", path, method); + ArrayList<String> rsp = mConnector.doCommand(cmd); - // tell mountd to unmount the media - mListener.ejectMedia(mountPath); + for (String line : rsp) { + String []tok = line.split(" "); + int code; + try { + code = Integer.parseInt(tok[0]); + } catch (NumberFormatException nfe) { + Slog.e(TAG, String.format("Error parsing code %s", tok[0])); + return false; + } + if (code == VoldResponseCode.ShareEnabledResult) { + if (tok[2].equals("enabled")) + return true; + return false; + } else { + Slog.e(TAG, String.format("Unexpected response code %d", code)); + return false; + } + } + Slog.e(TAG, "Got an empty response"); + return false; } - /** - * 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"); + private void notifyShareAvailabilityChange(String method, final boolean avail) { + if (!method.equals("ums")) { + Slog.w(TAG, "Ignoring unsupported share method {" + method + "}"); + return; + } + + synchronized (mListeners) { + for (int i = mListeners.size() -1; i >= 0; i--) { + MountServiceBinderListener bl = mListeners.get(i); + try { + bl.mListener.onUsbMassStorageConnectionChanged(avail); + } catch (RemoteException rex) { + Slog.e(TAG, "Listener dead"); + mListeners.remove(i); + } catch (Exception ex) { + Slog.e(TAG, "Listener failed", ex); + } + } + } + + if (mBooted == true) { + sendUmsIntent(avail); + } else { + mSendUmsConnectedOnBoot = avail; } - mListener.formatMedia(formatPath); + final String path = Environment.getExternalStorageDirectory().getPath(); + if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) { + /* + * USB mass storage disconnected while enabled + */ + new Thread() { + public void run() { + try { + int rc; + Slog.w(TAG, "Disabling UMS after cable disconnect"); + doShareUnshareVolume(path, "ums", false); + if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) { + Slog.e(TAG, String.format( + "Failed to remount {%s} on UMS enabled-disconnect (%d)", + path, rc)); + } + } catch (Exception ex) { + Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex); + } + } + }.start(); + } } - /** - * Returns true if we're playing media notification sounds. - */ - public boolean getPlayNotificationSounds() { - return mPlaySounds; + private void sendUmsIntent(boolean c) { + mContext.sendBroadcast( + new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED))); } - /** - * 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"); + private void validatePermission(String perm) { + if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(String.format("Requires %s permission", perm)); } - mPlaySounds = enabled; - SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0")); } /** - * Returns true if we auto-start UMS on cable insertion. + * Constructs a new MountService instance + * + * @param context Binder context for this service */ - public boolean getAutoStartUms() { - return mAutoStartUms; - } + public MountService(Context context) { + mContext = context; - /** - * 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"); + // XXX: This will go away soon in favor of IMountServiceObserver + mPms = (PackageManagerService) ServiceManager.getService("package"); + + mContext.registerReceiver(mBroadcastReceiver, + new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + + mHandlerThread = new HandlerThread("MountService"); + mHandlerThread.start(); + mHandler = new MountServiceHandler(mHandlerThread.getLooper()); + + /* + * Vold does not run in the simulator, so pretend the connector thread + * ran and did its thing. + */ + if ("simulator".equals(SystemProperties.get("ro.product.device"))) { + mReady = true; + mUmsEnabling = true; + return; } - mAutoStartUms = enabled; - SystemProperties.set("persist.service.mount.umsauto", (enabled ? "1" : "0")); + + mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector"); + mReady = false; + Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName()); + thread.start(); } /** - * Update the state of the USB mass storage notification + * Exposed API calls below here */ - void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) { - - try { - if (getMassStorageConnected() && !suppressIfConnected) { - Intent intent = new Intent(); - intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); - setUsbStorageNotification( - com.android.internal.R.string.usb_storage_notification_title, - 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); + public void registerListener(IMountServiceListener listener) { + synchronized (mListeners) { + MountServiceBinderListener bl = new MountServiceBinderListener(listener); + try { + listener.asBinder().linkToDeath(bl, 0); + mListeners.add(bl); + } catch (RemoteException rex) { + Slog.e(TAG, "Failed to link to listener death"); } - } 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); + public void unregisterListener(IMountServiceListener listener) { + synchronized (mListeners) { + for(MountServiceBinderListener bl : mListeners) { + if (bl.mListener == listener) { + mListeners.remove(mListeners.indexOf(bl)); + return; + } + } } } - /** - * 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) && - !storageState.equals(Environment.MEDIA_CHECKING)) { + public void shutdown(final IMountShutdownObserver observer) { + validatePermission(android.Manifest.permission.SHUTDOWN); + + Slog.i(TAG, "Shutting down"); + + String path = Environment.getExternalStorageDirectory().getPath(); + String state = getVolumeState(path); - if (mAutoStartUms) { + if (state.equals(Environment.MEDIA_SHARED)) { + /* + * If the media is currently shared, unshare it. + * XXX: This is still dangerous!. We should not + * be rebooting at *all* if UMS is enabled, since + * the UMS host could have dirty FAT cache entries + * yet to flush. + */ + setUsbMassStorageEnabled(false); + } else if (state.equals(Environment.MEDIA_CHECKING)) { + /* + * If the media is being checked, then we need to wait for + * it to complete before being able to proceed. + */ + // XXX: @hackbod - Should we disable the ANR timer here? + int retries = 30; + while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) { try { - setMassStorageEnabled(true); - } catch (RemoteException e) { + Thread.sleep(1000); + } catch (InterruptedException iex) { + Slog.e(TAG, "Interrupted while waiting for media", iex); + break; } - } else { - updateUsbMassStorageNotification(false, true); + state = Environment.getExternalStorageState(); + } + if (retries == 0) { + Slog.e(TAG, "Timed out waiting for media to check"); } } - Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED); - mContext.sendBroadcast(intent); + if (state.equals(Environment.MEDIA_MOUNTED)) { + // Post a unmount message. + ShutdownCallBack ucb = new ShutdownCallBack(path, observer); + mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb)); + } } - /** - * Broadcasts the USB mass storage disconnected event to all clients. - */ - void notifyUmsDisconnected() { - updateUsbMassStorageNotification(false, false); - Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); - mContext.sendBroadcast(intent); + private boolean getUmsEnabling() { + synchronized (mListeners) { + return mUmsEnabling; + } } - /** - * Broadcasts the media removed event to all clients. - */ - void notifyMediaRemoved(String path) { - updateUsbMassStorageNotification(true, false); + private void setUmsEnabling(boolean enable) { + synchronized (mListeners) { + mUmsEnabling = true; + } + } - setMediaStorageNotification( - com.android.internal.R.string.ext_media_nomedia_notification_title, - com.android.internal.R.string.ext_media_nomedia_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard_usb, - true, false, null); - handlePossibleExplicitUnmountBroadcast(path); + public boolean isUsbMassStorageConnected() { + waitForReady(); + + if (getUmsEnabling()) { + return true; + } + return doGetShareMethodAvailable("ums"); + } - Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, - Uri.parse("file://" + path)); - mContext.sendBroadcast(intent); + public void setUsbMassStorageEnabled(boolean enable) { + waitForReady(); + validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); + + // TODO: Add support for multiple share methods + + /* + * If the volume is mounted and we're enabling then unmount it + */ + String path = Environment.getExternalStorageDirectory().getPath(); + String vs = getVolumeState(path); + String method = "ums"; + if (enable && vs.equals(Environment.MEDIA_MOUNTED)) { + // Override for isUsbMassStorageEnabled() + setUmsEnabling(enable); + UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true); + mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb)); + // Clear override + setUmsEnabling(false); + } + /* + * If we disabled UMS then mount the volume + */ + if (!enable) { + doShareUnshareVolume(path, method, enable); + if (doMountVolume(path) != StorageResultCode.OperationSucceeded) { + Slog.e(TAG, "Failed to remount " + path + + " after disabling share method " + method); + /* + * Even though the mount failed, the unshare didn't so don't indicate an error. + * The mountVolume() call will have set the storage state and sent the necessary + * broadcasts. + */ + } + } } + public boolean isUsbMassStorageEnabled() { + waitForReady(); + return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums"); + } + /** - * Broadcasts the media unmounted event to all clients. + * @return state of the volume at the specified mount point */ - 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_sdcard, - true, true, null); - mShowSafeUnmountNotificationWhenUnmounted = false; - } else { - setMediaStorageNotification(0, 0, 0, false, false, null); + public String getVolumeState(String mountPoint) { + /* + * XXX: Until we have multiple volume discovery, just hardwire + * this to /sdcard + */ + if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) { + Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume"); + throw new IllegalArgumentException(); } - updateUsbMassStorageNotification(false, false); - Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, - Uri.parse("file://" + path)); - mContext.sendBroadcast(intent); + return mLegacyState; } - /** - * 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_sdcard_prepare, - true, false, null); + public int mountVolume(String path) { + validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - updateUsbMassStorageNotification(true, false); - Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, - Uri.parse("file://" + path)); - mContext.sendBroadcast(intent); + waitForReady(); + return doMountVolume(path); } - /** - * 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_notify_sdcard_usb, - true, false, pi); - updateUsbMassStorageNotification(false, false); - intent = new Intent(Intent.ACTION_MEDIA_NOFS, - Uri.parse("file://" + path)); - mContext.sendBroadcast(intent); + public void unmountVolume(String path, boolean force) { + validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); + waitForReady(); + + String volState = getVolumeState(path); + if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force); + if (Environment.MEDIA_UNMOUNTED.equals(volState) || + Environment.MEDIA_REMOVED.equals(volState) || + Environment.MEDIA_SHARED.equals(volState) || + Environment.MEDIA_UNMOUNTABLE.equals(volState)) { + // Media already unmounted or cannot be unmounted. + // TODO return valid return code when adding observer call back. + return; + } + UnmountCallBack ucb = new UnmountCallBack(path, force); + mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb)); } - /** - * 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); + public int formatVolume(String path) { + validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); + waitForReady(); + + return doFormatVolume(path); } - /** - * Broadcasts the media shared event to all clients. - */ - void notifyMediaShared(String path) { - 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); + public int []getStorageUsers(String path) { + validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); + waitForReady(); + try { + String[] r = mConnector.doListCommand( + String.format("storage users %s", path), + VoldResponseCode.StorageUsersListResult); + // FMT: <pid> <process name> + int[] data = new int[r.length]; + for (int i = 0; i < r.length; i++) { + String []tok = r[i].split(" "); + try { + data[i] = Integer.parseInt(tok[0]); + } catch (NumberFormatException nfe) { + Slog.e(TAG, String.format("Error parsing pid %s", tok[0])); + return new int[0]; + } + } + return data; + } catch (NativeDaemonConnectorException e) { + Slog.e(TAG, "Failed to retrieve storage users list", e); + return new int[0]; + } } - /** - * 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); + private void warnOnNotMounted() { + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + Slog.w(TAG, "getSecureContainerList() called when storage not mounted"); + } + } - handlePossibleExplicitUnmountBroadcast(path); - Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, - Uri.parse("file://" + path)); - mContext.sendBroadcast(intent); + public String[] getSecureContainerList() { + validatePermission(android.Manifest.permission.ASEC_ACCESS); + waitForReady(); + warnOnNotMounted(); - intent = new Intent(Intent.ACTION_MEDIA_REMOVED, - Uri.parse("file://" + path)); - mContext.sendBroadcast(intent); + try { + return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult); + } catch (NativeDaemonConnectorException e) { + return new String[0]; + } } - /** - * Broadcasts the media unmountable event to all clients. - */ - void notifyMediaUnmountable(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_unmountable_notification_title, - com.android.internal.R.string.ext_media_unmountable_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard_usb, - true, false, pi); - updateUsbMassStorageNotification(false, false); + public int createSecureContainer(String id, int sizeMb, String fstype, + String key, int ownerUid) { + validatePermission(android.Manifest.permission.ASEC_CREATE); + waitForReady(); + warnOnNotMounted(); - handlePossibleExplicitUnmountBroadcast(path); + int rc = StorageResultCode.OperationSucceeded; + String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + rc = StorageResultCode.OperationFailedInternalError; + } - intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, - Uri.parse("file://" + path)); - mContext.sendBroadcast(intent); - } - - /** - * Broadcasts the media eject event to all clients. - */ - void notifyMediaEject(String path) { - Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT, - Uri.parse("file://" + path)); - mContext.sendBroadcast(intent); + if (rc == StorageResultCode.OperationSucceeded) { + synchronized (mAsecMountSet) { + mAsecMountSet.add(id); + } + } + return rc; } - - /** - * Sets the USB storage notification. - */ - private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible, - PendingIntent pi) { - if (!visible && mUsbStorageNotification == null) { - return; + public int finalizeSecureContainer(String id) { + validatePermission(android.Manifest.permission.ASEC_CREATE); + warnOnNotMounted(); + + int rc = StorageResultCode.OperationSucceeded; + try { + mConnector.doCommand(String.format("asec finalize %s", id)); + /* + * Finalization does a remount, so no need + * to update mAsecMountSet + */ + } catch (NativeDaemonConnectorException e) { + rc = StorageResultCode.OperationFailedInternalError; } + return rc; + } - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); + public int destroySecureContainer(String id, boolean force) { + validatePermission(android.Manifest.permission.ASEC_DESTROY); + waitForReady(); + warnOnNotMounted(); - if (notificationManager == null) { - return; + int rc = StorageResultCode.OperationSucceeded; + try { + mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : ""))); + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedStorageBusy) { + rc = StorageResultCode.OperationFailedStorageBusy; + } else { + rc = StorageResultCode.OperationFailedInternalError; + } } - - if (visible) { - Resources r = Resources.getSystem(); - CharSequence title = r.getText(titleId); - CharSequence message = r.getText(messageId); - if (mUsbStorageNotification == null) { - mUsbStorageNotification = new Notification(); - mUsbStorageNotification.icon = icon; - mUsbStorageNotification.when = 0; + if (rc == StorageResultCode.OperationSucceeded) { + synchronized (mAsecMountSet) { + if (mAsecMountSet.contains(id)) { + mAsecMountSet.remove(id); + } } + } - if (sound && mPlaySounds) { - mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; - } else { - mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; + return rc; + } + + public int mountSecureContainer(String id, String key, int ownerUid) { + validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); + waitForReady(); + warnOnNotMounted(); + + synchronized (mAsecMountSet) { + if (mAsecMountSet.contains(id)) { + return StorageResultCode.OperationFailedStorageMounted; } - - mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; + } - mUsbStorageNotification.tickerText = title; - if (pi == null) { - Intent intent = new Intent(); - pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + int rc = StorageResultCode.OperationSucceeded; + String cmd = String.format("asec mount %s %s %d", id, key, ownerUid); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code != VoldResponseCode.OpFailedStorageBusy) { + rc = StorageResultCode.OperationFailedInternalError; } - - mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); } - - final int notificationId = mUsbStorageNotification.icon; - if (visible) { - notificationManager.notify(notificationId, mUsbStorageNotification); - } else { - notificationManager.cancel(notificationId); + + if (rc == StorageResultCode.OperationSucceeded) { + synchronized (mAsecMountSet) { + mAsecMountSet.add(id); + } } + return rc; } - private synchronized boolean getMediaStorageNotificationDismissable() { - if ((mMediaStorageNotification != null) && - ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) == - Notification.FLAG_AUTO_CANCEL)) - return true; + public int unmountSecureContainer(String id, boolean force) { + validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); + waitForReady(); + warnOnNotMounted(); - return false; - } + synchronized (mAsecMountSet) { + if (!mAsecMountSet.contains(id)) { + return StorageResultCode.OperationFailedStorageNotMounted; + } + } - /** - * Sets the media storage notification. - */ - private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, - boolean dismissable, PendingIntent pi) { + int rc = StorageResultCode.OperationSucceeded; + String cmd = String.format("asec unmount %s%s", id, (force ? " force" : "")); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedStorageBusy) { + rc = StorageResultCode.OperationFailedStorageBusy; + } else { + rc = StorageResultCode.OperationFailedInternalError; + } + } - if (!visible && mMediaStorageNotification == null) { - return; + if (rc == StorageResultCode.OperationSucceeded) { + synchronized (mAsecMountSet) { + mAsecMountSet.remove(id); + } } + return rc; + } - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); + public boolean isSecureContainerMounted(String id) { + validatePermission(android.Manifest.permission.ASEC_ACCESS); + waitForReady(); + warnOnNotMounted(); - if (notificationManager == null) { - return; + synchronized (mAsecMountSet) { + return mAsecMountSet.contains(id); } + } - if (mMediaStorageNotification != null && visible) { + public int renameSecureContainer(String oldId, String newId) { + validatePermission(android.Manifest.permission.ASEC_RENAME); + waitForReady(); + warnOnNotMounted(); + + synchronized (mAsecMountSet) { /* - * Dismiss the previous notification - we're about to - * re-use it. + * Because a mounted container has active internal state which cannot be + * changed while active, we must ensure both ids are not currently mounted. */ - final int notificationId = mMediaStorageNotification.icon; - notificationManager.cancel(notificationId); + if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) { + return StorageResultCode.OperationFailedStorageMounted; + } } - - 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; - } + int rc = StorageResultCode.OperationSucceeded; + String cmd = String.format("asec rename %s %s", oldId, newId); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + rc = StorageResultCode.OperationFailedInternalError; + } - if (mPlaySounds) { - mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND; - } else { - mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; - } + return rc; + } - if (dismissable) { - mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; - } else { - mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; - } + public String getSecureContainerPath(String id) { + validatePermission(android.Manifest.permission.ASEC_ACCESS); + waitForReady(); + warnOnNotMounted(); - mMediaStorageNotification.tickerText = title; - if (pi == null) { - Intent intent = new Intent(); - pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + try { + ArrayList<String> rsp = mConnector.doCommand(String.format("asec path %s", id)); + String []tok = rsp.get(0).split(" "); + int code = Integer.parseInt(tok[0]); + if (code != VoldResponseCode.AsecPathResult) { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + return tok[1]; + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedStorageNotFound) { + throw new IllegalArgumentException(String.format("Container '%s' not found", id)); + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); } - - mMediaStorageNotification.icon = icon; - mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi); - } - - final int notificationId = mMediaStorageNotification.icon; - if (visible) { - notificationManager.notify(notificationId, mMediaStorageNotification); - } else { - notificationManager.cancel(notificationId); } } + + public void finishMediaUpdate() { + mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); + } } diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java new file mode 100644 index 0000000..08d7ce6 --- /dev/null +++ b/services/java/com/android/server/NativeDaemonConnector.java @@ -0,0 +1,291 @@ +/* + * 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.net.LocalSocketAddress; +import android.net.LocalSocket; +import android.os.Environment; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.util.Slog; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +import java.util.List; +import java.util.ArrayList; +import java.util.ListIterator; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * Generic connector class for interfacing with a native + * daemon which uses the libsysutils FrameworkListener + * protocol. + */ +final class NativeDaemonConnector implements Runnable { + private static final boolean LOCAL_LOGD = false; + + private BlockingQueue<String> mResponseQueue; + private OutputStream mOutputStream; + private String TAG = "NativeDaemonConnector"; + private String mSocket; + private INativeDaemonConnectorCallbacks mCallbacks; + + private final int BUFFER_SIZE = 4096; + + class ResponseCode { + public static final int ActionInitiated = 100; + + public static final int CommandOkay = 200; + + // The range of 400 -> 599 is reserved for cmd failures + public static final int OperationFailed = 400; + public static final int CommandSyntaxError = 500; + public static final int CommandParameterError = 501; + + public static final int UnsolicitedInformational = 600; + + // + public static final int FailedRangeStart = 400; + public static final int FailedRangeEnd = 599; + } + + NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, + String socket, int responseQueueSize, String logTag) { + mCallbacks = callbacks; + if (logTag != null) + TAG = logTag; + mSocket = socket; + mResponseQueue = new LinkedBlockingQueue<String>(responseQueueSize); + } + + public void run() { + + while (true) { + try { + listenToSocket(); + } catch (Exception e) { + Slog.e(TAG, "Error in NativeDaemonConnector", e); + SystemClock.sleep(5000); + } + } + } + + private void listenToSocket() throws IOException { + LocalSocket socket = null; + + try { + socket = new LocalSocket(); + LocalSocketAddress address = new LocalSocketAddress(mSocket, + LocalSocketAddress.Namespace.RESERVED); + + socket.connect(address); + mCallbacks.onDaemonConnected(); + + InputStream inputStream = socket.getInputStream(); + mOutputStream = socket.getOutputStream(); + + byte[] buffer = new byte[BUFFER_SIZE]; + int start = 0; + + while (true) { + int count = inputStream.read(buffer, start, BUFFER_SIZE - start); + if (count < 0) break; + + for (int i = 0; i < count; i++) { + if (buffer[i] == 0) { + String event = new String(buffer, start, i - start); + if (LOCAL_LOGD) Slog.d(TAG, String.format("RCV <- {%s}", event)); + + String[] tokens = event.split(" "); + try { + int code = Integer.parseInt(tokens[0]); + + if (code >= ResponseCode.UnsolicitedInformational) { + try { + if (!mCallbacks.onEvent(code, event, tokens)) { + Slog.w(TAG, String.format( + "Unhandled event (%s)", event)); + } + } catch (Exception ex) { + Slog.e(TAG, String.format( + "Error handling '%s'", event), ex); + } + } else { + try { + mResponseQueue.put(event); + } catch (InterruptedException ex) { + Slog.e(TAG, "Failed to put response onto queue", ex); + } + } + } catch (NumberFormatException nfe) { + Slog.w(TAG, String.format("Bad msg (%s)", event)); + } + start = i + 1; + } + } + if (start != count) { + final int remaining = BUFFER_SIZE - start; + System.arraycopy(buffer, start, buffer, 0, remaining); + start = remaining; + } else { + start = 0; + } + } + } catch (IOException ex) { + Slog.e(TAG, "Communications error", ex); + throw ex; + } finally { + synchronized (this) { + if (mOutputStream != null) { + try { + mOutputStream.close(); + } catch (IOException e) { + Slog.w(TAG, "Failed closing output stream", e); + } + mOutputStream = null; + } + } + + try { + if (socket != null) { + socket.close(); + } + } catch (IOException ex) { + Slog.w(TAG, "Failed closing socket", ex); + } + } + } + + private void sendCommand(String command) { + sendCommand(command, null); + } + + /** + * Sends a command to the daemon with a single argument + * + * @param command The command to send to the daemon + * @param argument The argument to send with the command (or null) + */ + private void sendCommand(String command, String argument) { + synchronized (this) { + if (LOCAL_LOGD) Slog.d(TAG, String.format("SND -> {%s} {%s}", command, argument)); + if (mOutputStream == null) { + Slog.e(TAG, "No connection to daemon", new IllegalStateException()); + } else { + StringBuilder builder = new StringBuilder(command); + if (argument != null) { + builder.append(argument); + } + builder.append('\0'); + + try { + mOutputStream.write(builder.toString().getBytes()); + } catch (IOException ex) { + Slog.e(TAG, "IOException in sendCommand", ex); + } + } + } + } + + /** + * Issue a command to the native daemon and return the responses + */ + public synchronized ArrayList<String> doCommand(String cmd) + throws NativeDaemonConnectorException { + sendCommand(cmd); + + ArrayList<String> response = new ArrayList<String>(); + boolean complete = false; + int code = -1; + + while (!complete) { + try { + String line = mResponseQueue.take(); + if (LOCAL_LOGD) Slog.d(TAG, String.format("RSP <- {%s}", line)); + String[] tokens = line.split(" "); + try { + code = Integer.parseInt(tokens[0]); + } catch (NumberFormatException nfe) { + throw new NativeDaemonConnectorException( + String.format("Invalid response from daemon (%s)", line)); + } + + if ((code >= 200) && (code < 600)) { + complete = true; + } + response.add(line); + } catch (InterruptedException ex) { + Slog.e(TAG, "Failed to process response", ex); + } + } + + if (code >= ResponseCode.FailedRangeStart && + code <= ResponseCode.FailedRangeEnd) { + /* + * Note: The format of the last response in this case is + * "NNN <errmsg>" + */ + throw new NativeDaemonConnectorException( + code, cmd, response.get(response.size()-1).substring(4)); + } + return response; + } + + /* + * Issues a list command and returns the cooked list + */ + public String[] doListCommand(String cmd, int expectedResponseCode) + throws NativeDaemonConnectorException { + + ArrayList<String> rsp = doCommand(cmd); + String[] rdata = new String[rsp.size()-1]; + int idx = 0; + + for (int i = 0; i < rsp.size(); i++) { + String line = rsp.get(i); + try { + String[] tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == expectedResponseCode) { + rdata[idx++] = line.substring(tok[0].length() + 1); + } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { + if (LOCAL_LOGD) Slog.d(TAG, String.format("List terminated with {%s}", line)); + int last = rsp.size() -1; + if (i != last) { + Slog.w(TAG, String.format("Recv'd %d lines after end of list {%s}", (last-i), cmd)); + for (int j = i; j <= last ; j++) { + Slog.w(TAG, String.format("ExtraData <%s>", rsp.get(i))); + } + } + return rdata; + } else { + throw new NativeDaemonConnectorException( + String.format("Expected list response %d, but got %d", + expectedResponseCode, code)); + } + } catch (NumberFormatException nfe) { + throw new NativeDaemonConnectorException( + String.format("Error reading code '%s'", line)); + } + } + throw new NativeDaemonConnectorException("Got an empty response"); + } +} diff --git a/services/java/com/android/server/NativeDaemonConnectorException.java b/services/java/com/android/server/NativeDaemonConnectorException.java new file mode 100644 index 0000000..426742b --- /dev/null +++ b/services/java/com/android/server/NativeDaemonConnectorException.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +/** + * An exception that indicates there was an error with a NativeDaemonConnector operation + */ +public class NativeDaemonConnectorException extends RuntimeException +{ + private int mCode = -1; + private String mCmd; + + public NativeDaemonConnectorException() {} + + public NativeDaemonConnectorException(String error) + { + super(error); + } + + public NativeDaemonConnectorException(int code, String cmd, String error) + { + super(String.format("Cmd {%s} failed with code %d : {%s}", cmd, code, error)); + mCode = code; + mCmd = cmd; + } + + public int getCode() { + return mCode; + } + + public String getCmd() { + return mCmd; + } +} diff --git a/services/java/com/android/server/NetStatService.java b/services/java/com/android/server/NetStatService.java index 1ea0bac..7fe6743 100644 --- a/services/java/com/android/server/NetStatService.java +++ b/services/java/com/android/server/NetStatService.java @@ -17,44 +17,80 @@ package com.android.server; import android.content.Context; +import android.net.TrafficStats; import android.os.INetStatService; -import android.os.NetStat; +import android.os.SystemClock; + +import java.io.FileDescriptor; +import java.io.PrintWriter; public class NetStatService extends INetStatService.Stub { + private final Context mContext; public NetStatService(Context context) { - + mContext = context; } public long getMobileTxPackets() { - return NetStat.getMobileTxPkts(); + return TrafficStats.getMobileTxPackets(); } public long getMobileRxPackets() { - return NetStat.getMobileRxPkts(); + return TrafficStats.getMobileRxPackets(); } public long getMobileTxBytes() { - return NetStat.getMobileTxBytes(); + return TrafficStats.getMobileTxBytes(); } public long getMobileRxBytes() { - return NetStat.getMobileRxBytes(); + return TrafficStats.getMobileRxBytes(); } public long getTotalTxPackets() { - return NetStat.getTotalTxPkts(); + return TrafficStats.getTotalTxPackets(); } public long getTotalRxPackets() { - return NetStat.getTotalRxPkts(); + return TrafficStats.getTotalRxPackets(); } public long getTotalTxBytes() { - return NetStat.getTotalTxBytes(); + return TrafficStats.getTotalTxBytes(); } public long getTotalRxBytes() { - return NetStat.getTotalRxBytes(); + return TrafficStats.getTotalRxBytes(); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + // This data is accessible to any app -- no permission check needed. + + pw.print("Elapsed: total="); + pw.print(SystemClock.elapsedRealtime()); + pw.print("ms awake="); + pw.print(SystemClock.uptimeMillis()); + pw.println("ms"); + + pw.print("Mobile: Tx="); + pw.print(getMobileTxBytes()); + pw.print("B/"); + pw.print(getMobileTxPackets()); + pw.print("Pkts Rx="); + pw.print(getMobileRxBytes()); + pw.print("B/"); + pw.print(getMobileRxPackets()); + pw.println("Pkts"); + + pw.print("Total: Tx="); + pw.print(getTotalTxBytes()); + pw.print("B/"); + pw.print(getTotalTxPackets()); + pw.print("Pkts Rx="); + pw.print(getTotalRxBytes()); + pw.print("B/"); + pw.print(getTotalRxPackets()); + pw.println("Pkts"); } } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java new file mode 100644 index 0000000..552bed4 --- /dev/null +++ b/services/java/com/android/server/NetworkManagementService.java @@ -0,0 +1,608 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.net.InterfaceConfiguration; +import android.net.INetworkManagementEventObserver; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.KeyMgmt; +import android.os.INetworkManagementService; +import android.os.Handler; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; +import java.util.ArrayList; +import java.util.StringTokenizer; +import android.provider.Settings; +import android.content.ContentResolver; +import android.database.ContentObserver; + +import java.io.File; +import java.io.FileReader; +import java.lang.IllegalStateException; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * @hide + */ +class NetworkManagementService extends INetworkManagementService.Stub { + + private static final String TAG = "NetworkManagmentService"; + + class NetdResponseCode { + public static final int InterfaceListResult = 110; + public static final int TetherInterfaceListResult = 111; + public static final int TetherDnsFwdTgtListResult = 112; + public static final int TtyListResult = 113; + + public static final int TetherStatusResult = 210; + public static final int IpFwdStatusResult = 211; + public static final int InterfaceGetCfgResult = 213; + public static final int SoftapStatusResult = 214; + public static final int UsbRNDISStatusResult = 215; + public static final int InterfaceRxCounterResult = 216; + public static final int InterfaceTxCounterResult = 217; + public static final int InterfaceRxThrottleResult = 218; + public static final int InterfaceTxThrottleResult = 219; + + public static final int InterfaceChange = 600; + } + + /** + * Binder context for this service + */ + private Context mContext; + + /** + * connector object for communicating with netd + */ + private NativeDaemonConnector mConnector; + + private ArrayList<INetworkManagementEventObserver> mObservers; + + /** + * Constructs a new NetworkManagementService instance + * + * @param context Binder context for this service + */ + public NetworkManagementService(Context context) { + mContext = context; + + mObservers = new ArrayList<INetworkManagementEventObserver>(); + + if ("simulator".equals(SystemProperties.get("ro.product.device"))) { + return; + } + + mConnector = new NativeDaemonConnector( + new NetdCallbackReceiver(), "netd", 10, "NetdConnector"); + Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName()); + thread.start(); + } + + public void registerObserver(INetworkManagementEventObserver obs) { + Slog.d(TAG, "Registering observer"); + mObservers.add(obs); + } + + public void unregisterObserver(INetworkManagementEventObserver obs) { + Slog.d(TAG, "Unregistering observer"); + mObservers.remove(mObservers.indexOf(obs)); + } + + /** + * Notify our observers of an interface link status change + */ + private void notifyInterfaceLinkStatusChanged(String iface, boolean link) { + for (INetworkManagementEventObserver obs : mObservers) { + try { + obs.interfaceLinkStatusChanged(iface, link); + } catch (Exception ex) { + Slog.w(TAG, "Observer notifier failed", ex); + } + } + } + + /** + * Notify our observers of an interface addition. + */ + private void notifyInterfaceAdded(String iface) { + for (INetworkManagementEventObserver obs : mObservers) { + try { + obs.interfaceAdded(iface); + } catch (Exception ex) { + Slog.w(TAG, "Observer notifier failed", ex); + } + } + } + + /** + * Notify our observers of an interface removal. + */ + private void notifyInterfaceRemoved(String iface) { + for (INetworkManagementEventObserver obs : mObservers) { + try { + obs.interfaceRemoved(iface); + } catch (Exception ex) { + Slog.w(TAG, "Observer notifier failed", ex); + } + } + } + + + // + // Netd Callback handling + // + + class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { + public void onDaemonConnected() { + new Thread() { + public void run() { + } + }.start(); + } + public boolean onEvent(int code, String raw, String[] cooked) { + if (code == NetdResponseCode.InterfaceChange) { + /* + * a network interface change occured + * Format: "NNN Iface added <name>" + * "NNN Iface removed <name>" + * "NNN Iface changed <name> <up/down>" + */ + if (cooked.length < 4 || !cooked[1].equals("Iface")) { + throw new IllegalStateException( + String.format("Invalid event from daemon (%s)", raw)); + } + if (cooked[2].equals("added")) { + notifyInterfaceAdded(cooked[3]); + return true; + } else if (cooked[2].equals("removed")) { + notifyInterfaceRemoved(cooked[3]); + return true; + } else if (cooked[2].equals("changed") && cooked.length == 5) { + notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up")); + return true; + } + throw new IllegalStateException( + String.format("Invalid event from daemon (%s)", raw)); + } + return false; + } + } + + private static int stringToIpAddr(String addrString) throws UnknownHostException { + try { + String[] parts = addrString.split("\\."); + if (parts.length != 4) { + throw new UnknownHostException(addrString); + } + + int a = Integer.parseInt(parts[0]) ; + int b = Integer.parseInt(parts[1]) << 8; + int c = Integer.parseInt(parts[2]) << 16; + int d = Integer.parseInt(parts[3]) << 24; + + return a | b | c | d; + } catch (NumberFormatException ex) { + throw new UnknownHostException(addrString); + } + } + + public static String intToIpString(int i) { + return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >> 8 ) & 0xFF) + "." + + (i & 0xFF); + } + + // + // INetworkManagementService members + // + + public String[] listInterfaces() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult); + } + + public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException { + String rsp = mConnector.doCommand("interface getcfg " + iface).get(0); + Slog.d(TAG, String.format("rsp <%s>", rsp)); + + // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3] + StringTokenizer st = new StringTokenizer(rsp); + + try { + int code = Integer.parseInt(st.nextToken(" ")); + if (code != NetdResponseCode.InterfaceGetCfgResult) { + throw new IllegalStateException( + String.format("Expected code %d, but got %d", + NetdResponseCode.InterfaceGetCfgResult, code)); + } + } catch (NumberFormatException nfe) { + throw new IllegalStateException( + String.format("Invalid response from daemon (%s)", rsp)); + } + + InterfaceConfiguration cfg = new InterfaceConfiguration(); + cfg.hwAddr = st.nextToken(" "); + try { + cfg.ipAddr = stringToIpAddr(st.nextToken(" ")); + } catch (UnknownHostException uhe) { + Slog.e(TAG, "Failed to parse ipaddr", uhe); + cfg.ipAddr = 0; + } + + try { + cfg.netmask = stringToIpAddr(st.nextToken(" ")); + } catch (UnknownHostException uhe) { + Slog.e(TAG, "Failed to parse netmask", uhe); + cfg.netmask = 0; + } + cfg.interfaceFlags = st.nextToken("]").trim() +"]"; + Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags)); + return cfg; + } + + public void setInterfaceConfig( + String iface, InterfaceConfiguration cfg) throws IllegalStateException { + String cmd = String.format("interface setcfg %s %s %s %s", iface, + intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags); + mConnector.doCommand(cmd); + } + + public void shutdown() { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.SHUTDOWN) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires SHUTDOWN permission"); + } + + Slog.d(TAG, "Shutting down"); + } + + public boolean getIpForwardingEnabled() throws IllegalStateException{ + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList<String> rsp = mConnector.doCommand("ipfwd status"); + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.IpFwdStatusResult) { + // 211 Forwarding <enabled/disabled> + if (tok.length !=2) { + throw new IllegalStateException( + String.format("Malformatted list entry '%s'", line)); + } + if (tok[2].equals("enabled")) + return true; + return false; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void setIpForwardingEnabled(boolean enable) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); + } + + public void startTethering(String[] dhcpRange) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + // cmd is "tether start first_start first_stop second_start second_stop ..." + // an odd number of addrs will fail + String cmd = "tether start"; + for (String d : dhcpRange) { + cmd += " " + d; + } + mConnector.doCommand(cmd); + } + + public void stopTethering() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("tether stop"); + } + + public boolean isTetheringStarted() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList<String> rsp = mConnector.doCommand("tether status"); + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.TetherStatusResult) { + // XXX: Tethering services <started/stopped> <TBD>... + if (tok[2].equals("started")) + return true; + return false; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void tetherInterface(String iface) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("tether interface add " + iface); + } + + public void untetherInterface(String iface) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("tether interface remove " + iface); + } + + public String[] listTetheredInterfaces() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + return mConnector.doListCommand( + "tether interface list", NetdResponseCode.TetherInterfaceListResult); + } + + public void setDnsForwarders(String[] dns) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + try { + String cmd = "tether dns set"; + for (String s : dns) { + cmd += " " + InetAddress.getByName(s).getHostAddress(); + } + mConnector.doCommand(cmd); + } catch (UnknownHostException e) { + throw new IllegalStateException("Error resolving dns name", e); + } + } + + public String[] getDnsForwarders() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + return mConnector.doListCommand( + "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult); + } + + public void enableNat(String internalInterface, String externalInterface) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand( + String.format("nat enable %s %s", internalInterface, externalInterface)); + } + + public void disableNat(String internalInterface, String externalInterface) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand( + String.format("nat disable %s %s", internalInterface, externalInterface)); + } + + public String[] listTtys() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult); + } + + public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr, + String dns2Addr) throws IllegalStateException { + try { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty, + InetAddress.getByName(localAddr).getHostAddress(), + InetAddress.getByName(remoteAddr).getHostAddress(), + InetAddress.getByName(dns1Addr).getHostAddress(), + InetAddress.getByName(dns2Addr).getHostAddress())); + } catch (UnknownHostException e) { + throw new IllegalStateException("Error resolving addr", e); + } + } + + public void detachPppd(String tty) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand(String.format("pppd detach %s", tty)); + } + + public void startUsbRNDIS() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("usb startrndis"); + } + + public void stopUsbRNDIS() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("usb stoprndis"); + } + + public boolean isUsbRNDISStarted() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + ArrayList<String> rsp = mConnector.doCommand("usb rndisstatus"); + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.UsbRNDISStatusResult) { + if (tok[3].equals("started")) + return true; + return false; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + mConnector.doCommand(String.format("softap stop " + wlanIface)); + mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP")); + mConnector.doCommand(String.format("softap start " + wlanIface)); + if (wifiConfig == null) { + mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); + } else { + /** + * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8] + * argv1 - wlan interface + * argv2 - softap interface + * argv3 - SSID + * argv4 - Security + * argv5 - Key + * argv6 - Channel + * argv7 - Preamble + * argv8 - Max SCB + */ + String str = String.format("softap set " + wlanIface + " " + softapIface + + " \"%s\" %s %s", wifiConfig.SSID, + wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? + "wpa2-psk" : "open", + wifiConfig.preSharedKey); + mConnector.doCommand(str); + } + mConnector.doCommand(String.format("softap startap")); + } + + public void stopAccessPoint() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + mConnector.doCommand("softap stopap"); + } + + public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + if (wifiConfig == null) { + mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); + } else { + String str = String.format("softap set " + wlanIface + " " + softapIface + + " \"%s\" %s %s", wifiConfig.SSID, + wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? + "wpa2-psk" : "open", + wifiConfig.preSharedKey); + mConnector.doCommand(str); + } + } + + private long getInterfaceCounter(String iface, boolean rx) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + try { + String rsp = mConnector.doCommand( + String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0); + String []tok = rsp.split(" "); + int code; + try { + code = Integer.parseInt(tok[0]); + } catch (NumberFormatException nfe) { + Slog.e(TAG, String.format("Error parsing code %s", tok[0])); + return -1; + } + if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || ( + !rx && code != NetdResponseCode.InterfaceTxCounterResult)) { + Slog.e(TAG, String.format("Unexpected response code %d", code)); + return -1; + } + return Long.parseLong(tok[1]); + } catch (Exception e) { + Slog.e(TAG, String.format( + "Failed to read interface %s counters", (rx ? "rx" : "tx")), e); + } + return -1; + } + + public long getInterfaceRxCounter(String iface) { + return getInterfaceCounter(iface, true); + } + + public long getInterfaceTxCounter(String iface) { + return getInterfaceCounter(iface, false); + } + + public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand(String.format( + "interface setthrottle %s %d %d", iface, rxKbps, txKbps)); + } + + private int getInterfaceThrottle(String iface, boolean rx) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + try { + String rsp = mConnector.doCommand( + String.format("interface getthrottle %s %s", iface,(rx ? "rx" : "tx"))).get(0); + String []tok = rsp.split(" "); + int code; + try { + code = Integer.parseInt(tok[0]); + } catch (NumberFormatException nfe) { + Slog.e(TAG, String.format("Error parsing code %s", tok[0])); + return -1; + } + if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || ( + !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) { + Slog.e(TAG, String.format("Unexpected response code %d", code)); + return -1; + } + return Integer.parseInt(tok[1]); + } catch (Exception e) { + Slog.e(TAG, String.format( + "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e); + } + return -1; + } + + public int getInterfaceRxThrottle(String iface) { + return getInterfaceThrottle(iface, true); + } + + public int getInterfaceTxThrottle(String iface) { + return getInterfaceThrottle(iface, false); + } +} diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index d7e1d25..73d17ea 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -39,7 +39,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.ContentObserver; -import android.media.AsyncPlayer; import android.media.AudioManager; import android.net.Uri; import android.os.BatteryManager; @@ -53,8 +52,10 @@ import android.os.RemoteException; import android.os.SystemProperties; import android.os.Vibrator; import android.provider.Settings; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.EventLog; +import android.util.Slog; import android.util.Log; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -75,8 +76,8 @@ class NotificationManagerService extends INotificationManager.Stub private static final int LONG_DELAY = 3500; // 3.5 seconds private static final int SHORT_DELAY = 2000; // 2 seconds - - private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; + + private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; @@ -86,10 +87,17 @@ class NotificationManagerService extends INotificationManager.Stub private WorkerHandler mHandler; private StatusBarService mStatusBarService; - private HardwareService mHardware; + private LightsService mLightsService; + private LightsService.Light mBatteryLight; + private LightsService.Light mNotificationLight; + private LightsService.Light mAttentionLight; + + private int mDefaultNotificationColor; + private int mDefaultNotificationLedOn; + private int mDefaultNotificationLedOff; private NotificationRecord mSoundNotification; - private AsyncPlayer mSound; + private NotificationPlayer mSound; private boolean mSystemReady; private int mDisabledNotifications; @@ -98,6 +106,7 @@ class NotificationManagerService extends INotificationManager.Stub // for enabling and disabling notification pulse behavior private boolean mScreenOn = true; + private boolean mInCall = false; private boolean mNotificationPulseEnabled; // for adb connected notifications @@ -105,7 +114,7 @@ class NotificationManagerService extends INotificationManager.Stub private boolean mAdbEnabled = false; private boolean mAdbNotificationShown = false; private Notification mAdbNotification; - + private final ArrayList<NotificationRecord> mNotificationList = new ArrayList<NotificationRecord>(); @@ -124,11 +133,6 @@ class NotificationManagerService extends INotificationManager.Stub private static final int BATTERY_BLINK_ON = 125; private static final int BATTERY_BLINK_OFF = 2875; - // Tag IDs for EventLog. - private static final int EVENT_LOG_ENQUEUE = 2750; - private static final int EVENT_LOG_CANCEL = 2751; - private static final int EVENT_LOG_CANCEL_ALL = 2752; - private static String idDebugString(Context baseContext, String packageName, int id) { Context c = null; @@ -188,7 +192,7 @@ class NotificationManagerService extends INotificationManager.Stub + " ledOnMS=" + notification.ledOnMS + " ledOffMS=" + notification.ledOffMS); } - + @Override public final String toString() { @@ -218,11 +222,11 @@ class NotificationManagerService extends INotificationManager.Stub void update(int duration) { this.duration = duration; } - + void dump(PrintWriter pw, String prefix) { pw.println(prefix + this); } - + @Override public final String toString() { @@ -305,6 +309,8 @@ class NotificationManagerService extends INotificationManager.Stub public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + boolean queryRestart = false; + if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0); int level = intent.getIntExtra("level", -1); @@ -327,22 +333,39 @@ class NotificationManagerService extends INotificationManager.Stub mUsbConnected = false; updateAdbNotification(); } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) - || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) { - Uri uri = intent.getData(); - if (uri == null) { - return; + || action.equals(Intent.ACTION_PACKAGE_RESTARTED) + || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) + || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { + String pkgList[] = null; + if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + } else if (queryRestart) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); + } else { + Uri uri = intent.getData(); + if (uri == null) { + return; + } + String pkgName = uri.getSchemeSpecificPart(); + if (pkgName == null) { + return; + } + pkgList = new String[]{pkgName}; } - String pkgName = uri.getSchemeSpecificPart(); - if (pkgName == null) { - return; + if (pkgList != null && (pkgList.length > 0)) { + for (String pkgName : pkgList) { + cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart); + } } - cancelAllNotificationsInt(pkgName, 0, 0); } else if (action.equals(Intent.ACTION_SCREEN_ON)) { mScreenOn = true; updateNotificationPulse(); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; updateNotificationPulse(); + } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { + mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK)); + updateNotificationPulse(); } } }; @@ -351,7 +374,7 @@ class NotificationManagerService extends INotificationManager.Stub SettingsObserver(Handler handler) { super(handler); } - + void observe() { ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(Settings.Secure.getUriFor( @@ -383,19 +406,32 @@ class NotificationManagerService extends INotificationManager.Stub } NotificationManagerService(Context context, StatusBarService statusBar, - HardwareService hardware) + LightsService lights) { super(); mContext = context; - mHardware = hardware; + mLightsService = lights; mAm = ActivityManagerNative.getDefault(); - mSound = new AsyncPlayer(TAG); + mSound = new NotificationPlayer(TAG); mSound.setUsesWakeLock(context); mToastQueue = new ArrayList<ToastRecord>(); mHandler = new WorkerHandler(); + mStatusBarService = statusBar; statusBar.setNotificationCallbacks(mNotificationCallbacks); + mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY); + mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS); + mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); + + Resources resources = mContext.getResources(); + mDefaultNotificationColor = resources.getColor( + com.android.internal.R.color.config_defaultNotificationColor); + mDefaultNotificationLedOn = resources.getInteger( + com.android.internal.R.integer.config_defaultNotificationLedOn); + mDefaultNotificationLedOff = resources.getInteger( + com.android.internal.R.integer.config_defaultNotificationLedOff); + // Don't start allowing notifications until the setup wizard has run once. // After that, including subsequent boots, init with notifications turned on. // This works on the first boot because the setup wizard will toggle this @@ -410,12 +446,19 @@ class NotificationManagerService extends INotificationManager.Stub filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(Intent.ACTION_UMS_CONNECTED); filter.addAction(Intent.ACTION_UMS_DISCONNECTED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); mContext.registerReceiver(mIntentReceiver, filter); - + IntentFilter pkgFilter = new IntentFilter(); + pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); + pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); + pkgFilter.addDataScheme("package"); + mContext.registerReceiver(mIntentReceiver, pkgFilter); + IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + mContext.registerReceiver(mIntentReceiver, sdFilter); + SettingsObserver observer = new SettingsObserver(mHandler); observer.observe(); } @@ -429,10 +472,10 @@ class NotificationManagerService extends INotificationManager.Stub // ============================================================================ public void enqueueToast(String pkg, ITransientNotification callback, int duration) { - Log.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); + if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); if (pkg == null || callback == null) { - Log.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); + Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); return ; } @@ -467,10 +510,10 @@ class NotificationManagerService extends INotificationManager.Stub } public void cancelToast(String pkg, ITransientNotification callback) { - Log.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); + Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); if (pkg == null || callback == null) { - Log.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); + Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); return ; } @@ -481,7 +524,7 @@ class NotificationManagerService extends INotificationManager.Stub if (index >= 0) { cancelToastLocked(index); } else { - Log.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); + Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); } } finally { Binder.restoreCallingIdentity(callingId); @@ -492,13 +535,13 @@ class NotificationManagerService extends INotificationManager.Stub private void showNextToastLocked() { ToastRecord record = mToastQueue.get(0); while (record != null) { - if (DBG) Log.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); + if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); try { record.callback.show(); scheduleTimeoutLocked(record, false); return; } catch (RemoteException e) { - Log.w(TAG, "Object died trying to show notification " + record.callback + Slog.w(TAG, "Object died trying to show notification " + record.callback + " in package " + record.pkg); // remove it from the list and let the process die int index = mToastQueue.indexOf(record); @@ -520,7 +563,7 @@ class NotificationManagerService extends INotificationManager.Stub try { record.callback.hide(); } catch (RemoteException e) { - Log.w(TAG, "Object died trying to hide notification " + record.callback + Slog.w(TAG, "Object died trying to hide notification " + record.callback + " in package " + record.pkg); // don't worry about this, we're about to remove it from // the list anyway @@ -545,7 +588,7 @@ class NotificationManagerService extends INotificationManager.Stub private void handleTimeout(ToastRecord record) { - if (DBG) Log.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); + if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); synchronized (mToastQueue) { int index = indexOfToastLocked(record.pkg, record.callback); if (index >= 0) { @@ -614,12 +657,12 @@ class NotificationManagerService extends INotificationManager.Stub Notification notification, int[] idOut) { checkIncomingCall(pkg); - + // This conditional is a dirty hack to limit the logging done on // behalf of the download manager without affecting other apps. if (!pkg.equals("com.android.providers.downloads") || Log.isLoggable("DownloadManager", Log.VERBOSE)) { - EventLog.writeEvent(EVENT_LOG_ENQUEUE, pkg, id, notification.toString()); + EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, notification.toString()); } if (pkg == null || notification == null) { @@ -653,20 +696,20 @@ class NotificationManagerService extends INotificationManager.Stub old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE; } } - + // Ensure if this is a foreground service that the proper additional // flags are set. if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR; } - + if (notification.icon != 0) { IconData icon = IconData.makeIcon(null, pkg, notification.icon, notification.iconLevel, notification.number); CharSequence truncatedTicker = notification.tickerText; - + // TODO: make this restriction do something smarter like never fill // more than two screens. "Why would anyone need more than 80 characters." :-/ final int maxTickerLen = 80; @@ -700,7 +743,7 @@ class NotificationManagerService extends INotificationManager.Stub long identity = Binder.clearCallingIdentity(); try { r.statusBarKey = mStatusBarService.addIcon(icon, n); - mHardware.pulseBreathingLight(); + mAttentionLight.pulse(); } finally { Binder.restoreCallingIdentity(identity); @@ -731,7 +774,7 @@ class NotificationManagerService extends INotificationManager.Stub .getSystemService(Context.AUDIO_SERVICE); // sound final boolean useDefaultSound = - (notification.defaults & Notification.DEFAULT_SOUND) != 0; + (notification.defaults & Notification.DEFAULT_SOUND) != 0; if (useDefaultSound || notification.sound != null) { Uri uri; if (useDefaultSound) { @@ -762,12 +805,12 @@ class NotificationManagerService extends INotificationManager.Stub // vibrate final boolean useDefaultVibrate = - (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; + (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; if ((useDefaultVibrate || notification.vibrate != null) && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) { mVibrateNotification = r; - mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN + mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN : notification.vibrate, ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); } @@ -781,7 +824,7 @@ class NotificationManagerService extends INotificationManager.Stub if (mLedNotification == old) { mLedNotification = null; } - //Log.i(TAG, "notification.lights=" + //Slog.i(TAG, "notification.lights=" // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { mLights.add(r); @@ -862,24 +905,24 @@ class NotificationManagerService extends INotificationManager.Stub /** * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} - * and none of the {@code mustNotHaveFlags}. + * and none of the {@code mustNotHaveFlags}. */ private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, int mustNotHaveFlags) { - EventLog.writeEvent(EVENT_LOG_CANCEL, pkg, id, mustHaveFlags); + EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags); synchronized (mNotificationList) { int index = indexOfNotificationLocked(pkg, tag, id); if (index >= 0) { NotificationRecord r = mNotificationList.get(index); - + if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { return; } if ((r.notification.flags & mustNotHaveFlags) != 0) { return; } - + mNotificationList.remove(index); cancelNotificationLocked(r); @@ -892,9 +935,9 @@ class NotificationManagerService extends INotificationManager.Stub * Cancels all notifications from a given package that have all of the * {@code mustHaveFlags}. */ - void cancelAllNotificationsInt(String pkg, int mustHaveFlags, - int mustNotHaveFlags) { - EventLog.writeEvent(EVENT_LOG_CANCEL_ALL, pkg, mustHaveFlags); + boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, + int mustNotHaveFlags, boolean doit) { + EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags); synchronized (mNotificationList) { final int N = mNotificationList.size(); @@ -910,17 +953,21 @@ class NotificationManagerService extends INotificationManager.Stub if (!r.pkg.equals(pkg)) { continue; } + canceledSomething = true; + if (!doit) { + return true; + } mNotificationList.remove(i); cancelNotificationLocked(r); - canceledSomething = true; } if (canceledSomething) { updateLightsLocked(); } + return canceledSomething; } } - + public void cancelNotification(String pkg, int id) { cancelNotificationWithTag(pkg, null /* tag */, id); } @@ -935,10 +982,10 @@ class NotificationManagerService extends INotificationManager.Stub public void cancelAllNotifications(String pkg) { checkIncomingCall(pkg); - + // Calling from user space, don't allow the canceling of actively // running foreground services. - cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE); + cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true); } void checkIncomingCall(String pkg) { @@ -957,7 +1004,7 @@ class NotificationManagerService extends INotificationManager.Stub throw new SecurityException("Unknown package " + pkg); } } - + void cancelAll() { synchronized (mNotificationList) { final int N = mNotificationList.size(); @@ -972,7 +1019,7 @@ class NotificationManagerService extends INotificationManager.Stub } catch (PendingIntent.CanceledException ex) { // do nothing - there's no relevant way to recover, and // no reason to let this propagate - Log.w(TAG, "canceled PendingIntent for " + r.pkg, ex); + Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex); } } mNotificationList.remove(i); @@ -996,24 +1043,20 @@ class NotificationManagerService extends INotificationManager.Stub // Battery low always shows, other states only show if charging. if (mBatteryLow) { if (mBatteryCharging) { - mHardware.setLightColor_UNCHECKED(HardwareService.LIGHT_ID_BATTERY, - BATTERY_LOW_ARGB); + mBatteryLight.setColor(BATTERY_LOW_ARGB); } else { // Flash when battery is low and not charging - mHardware.setLightFlashing_UNCHECKED(HardwareService.LIGHT_ID_BATTERY, - BATTERY_LOW_ARGB, HardwareService.LIGHT_FLASH_TIMED, - BATTERY_BLINK_ON, BATTERY_BLINK_OFF); + mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED, + BATTERY_BLINK_ON, BATTERY_BLINK_OFF); } } else if (mBatteryCharging) { if (mBatteryFull) { - mHardware.setLightColor_UNCHECKED(HardwareService.LIGHT_ID_BATTERY, - BATTERY_FULL_ARGB); + mBatteryLight.setColor(BATTERY_FULL_ARGB); } else { - mHardware.setLightColor_UNCHECKED(HardwareService.LIGHT_ID_BATTERY, - BATTERY_MEDIUM_ARGB); + mBatteryLight.setColor(BATTERY_MEDIUM_ARGB); } } else { - mHardware.setLightOff_UNCHECKED(HardwareService.LIGHT_ID_BATTERY); + mBatteryLight.turnOff(); } // handle notification lights @@ -1026,15 +1069,26 @@ class NotificationManagerService extends INotificationManager.Stub } // we only flash if screen is off and persistent pulsing is enabled - if (mLedNotification == null || mScreenOn || !mNotificationPulseEnabled) { - mHardware.setLightOff_UNCHECKED(HardwareService.LIGHT_ID_NOTIFICATIONS); + // and we are not currently in a call + if (mLedNotification == null || mScreenOn || mInCall) { + mNotificationLight.turnOff(); } else { - mHardware.setLightFlashing_UNCHECKED( - HardwareService.LIGHT_ID_NOTIFICATIONS, - mLedNotification.notification.ledARGB, - HardwareService.LIGHT_FLASH_TIMED, - mLedNotification.notification.ledOnMS, - mLedNotification.notification.ledOffMS); + int ledARGB = mLedNotification.notification.ledARGB; + int ledOnMS = mLedNotification.notification.ledOnMS; + int ledOffMS = mLedNotification.notification.ledOffMS; + if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { + ledARGB = mDefaultNotificationColor; + ledOnMS = mDefaultNotificationLedOn; + ledOffMS = mDefaultNotificationLedOff; + } + if (mNotificationPulseEnabled) { + // pulse repeatedly + mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, + ledOnMS, ledOffMS); + } else { + // pulse only once + mNotificationLight.pulse(ledARGB, ledOnMS); + } } } @@ -1081,11 +1135,13 @@ class NotificationManagerService extends INotificationManager.Stub if (mAdbNotification == null) { mAdbNotification = new Notification(); - mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning; + mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb; mAdbNotification.when = 0; mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT; mAdbNotification.tickerText = title; - mAdbNotification.defaults |= Notification.DEFAULT_SOUND; + mAdbNotification.defaults = 0; // please be quiet + mAdbNotification.sound = null; + mAdbNotification.vibrate = null; } Intent intent = new Intent( @@ -1101,14 +1157,14 @@ class NotificationManagerService extends INotificationManager.Stub intent, 0); mAdbNotification.setLatestEventInfo(mContext, title, message, pi); - + mAdbNotificationShown = true; notificationManager.notify( com.android.internal.R.string.adb_active_notification_title, mAdbNotification); } } - + } else if (mAdbNotificationShown) { NotificationManager notificationManager = (NotificationManager) mContext .getSystemService(Context.NOTIFICATION_SERVICE); @@ -1136,7 +1192,7 @@ class NotificationManagerService extends INotificationManager.Stub + ", uid=" + Binder.getCallingUid()); return; } - + pw.println("Current Notification Manager state:"); int N; @@ -1150,7 +1206,7 @@ class NotificationManagerService extends INotificationManager.Stub } pw.println(" "); } - + } synchronized (mNotificationList) { @@ -1162,7 +1218,7 @@ class NotificationManagerService extends INotificationManager.Stub } pw.println(" "); } - + N = mLights.size(); if (N > 0) { pw.println(" Lights List:"); @@ -1171,7 +1227,7 @@ class NotificationManagerService extends INotificationManager.Stub } pw.println(" "); } - + pw.println(" mSoundNotification=" + mSoundNotification); pw.println(" mSound=" + mSound); pw.println(" mVibrateNotification=" + mVibrateNotification); diff --git a/services/java/com/android/server/NotificationPlayer.java b/services/java/com/android/server/NotificationPlayer.java new file mode 100644 index 0000000..0b1a03b --- /dev/null +++ b/services/java/com/android/server/NotificationPlayer.java @@ -0,0 +1,338 @@ +/* + * 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.media.AudioManager; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.SystemClock; +import android.util.Log; + +import java.io.IOException; +import java.lang.IllegalStateException; +import java.lang.Thread; +import java.util.LinkedList; + +/** + * @hide + * This class is provides the same interface and functionality as android.media.AsyncPlayer + * with the following differences: + * - whenever audio is played, audio focus is requested, + * - whenever audio playback is stopped or the playback completed, audio focus is abandoned. + */ +public class NotificationPlayer implements OnCompletionListener { + private static final int PLAY = 1; + private static final int STOP = 2; + private static final boolean mDebug = false; + + private static final class Command { + int code; + Context context; + Uri uri; + boolean looping; + int stream; + long requestTime; + + public String toString() { + return "{ code=" + code + " looping=" + looping + " stream=" + stream + + " uri=" + uri + " }"; + } + } + + private LinkedList<Command> mCmdQueue = new LinkedList(); + + private Looper mLooper; + + /* + * Besides the use of audio focus, the only implementation difference between AsyncPlayer and + * NotificationPlayer resides in the creation of the MediaPlayer. For the completion callback, + * OnCompletionListener, to be called at the end of the playback, the MediaPlayer needs to + * be created with a looper running so its event handler is not null. + */ + private final class CreationAndCompletionThread extends Thread { + public Command mCmd; + public CreationAndCompletionThread(Command cmd) { + super(); + mCmd = cmd; + } + + public void run() { + Looper.prepare(); + mLooper = Looper.myLooper(); + synchronized(this) { + AudioManager audioManager = + (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE); + try { + MediaPlayer player = new MediaPlayer(); + player.setAudioStreamType(mCmd.stream); + player.setDataSource(mCmd.context, mCmd.uri); + player.setLooping(mCmd.looping); + player.prepare(); + if (mCmd.looping) { + audioManager.requestAudioFocus(null, mCmd.stream, + AudioManager.AUDIOFOCUS_GAIN); + } else { + audioManager.requestAudioFocus(null, mCmd.stream, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); + } + player.setOnCompletionListener(NotificationPlayer.this); + player.start(); + if (mPlayer != null) { + mPlayer.release(); + } + mPlayer = player; + } + catch (Exception e) { + Log.w(mTag, "error loading sound for " + mCmd.uri, e); + } + mAudioManager = audioManager; + this.notify(); + } + Looper.loop(); + } + }; + + private void startSound(Command cmd) { + // Preparing can be slow, so if there is something else + // is playing, let it continue until we're done, so there + // is less of a glitch. + try { + if (mDebug) Log.d(mTag, "Starting playback"); + //----------------------------------- + // This is were we deviate from the AsyncPlayer implementation and create the + // MediaPlayer in a new thread with which we're synchronized + synchronized(mCompletionHandlingLock) { + // if another sound was already playing, it doesn't matter we won't get notified + // of the completion, since only the completion notification of the last sound + // matters + if((mLooper != null) + && (mLooper.getThread().getState() != Thread.State.TERMINATED)) { + mLooper.quit(); + } + mCompletionThread = new CreationAndCompletionThread(cmd); + synchronized(mCompletionThread) { + mCompletionThread.start(); + mCompletionThread.wait(); + } + } + //----------------------------------- + + long delay = SystemClock.uptimeMillis() - cmd.requestTime; + if (delay > 1000) { + Log.w(mTag, "Notification sound delayed by " + delay + "msecs"); + } + } + catch (Exception e) { + Log.w(mTag, "error loading sound for " + cmd.uri, e); + } + } + + private final class CmdThread extends java.lang.Thread { + CmdThread() { + super("NotificationPlayer-" + mTag); + } + + public void run() { + while (true) { + Command cmd = null; + + synchronized (mCmdQueue) { + if (mDebug) Log.d(mTag, "RemoveFirst"); + cmd = mCmdQueue.removeFirst(); + } + + switch (cmd.code) { + case PLAY: + if (mDebug) Log.d(mTag, "PLAY"); + startSound(cmd); + break; + case STOP: + if (mDebug) Log.d(mTag, "STOP"); + if (mPlayer != null) { + long delay = SystemClock.uptimeMillis() - cmd.requestTime; + if (delay > 1000) { + Log.w(mTag, "Notification stop delayed by " + delay + "msecs"); + } + mPlayer.stop(); + mPlayer.release(); + mPlayer = null; + mAudioManager.abandonAudioFocus(null); + mAudioManager = null; + if((mLooper != null) + && (mLooper.getThread().getState() != Thread.State.TERMINATED)) { + mLooper.quit(); + } + } else { + Log.w(mTag, "STOP command without a player"); + } + break; + } + + synchronized (mCmdQueue) { + if (mCmdQueue.size() == 0) { + // nothing left to do, quit + // doing this check after we're done prevents the case where they + // added it during the operation from spawning two threads and + // trying to do them in parallel. + mThread = null; + releaseWakeLock(); + return; + } + } + } + } + } + + public void onCompletion(MediaPlayer mp) { + if (mAudioManager != null) { + mAudioManager.abandonAudioFocus(null); + } + // if there are no more sounds to play, end the Looper to listen for media completion + synchronized (mCmdQueue) { + if (mCmdQueue.size() == 0) { + synchronized(mCompletionHandlingLock) { + if(mLooper != null) { + mLooper.quit(); + } + mCompletionThread = null; + } + } + } + } + + private String mTag; + private CmdThread mThread; + private CreationAndCompletionThread mCompletionThread; + private final Object mCompletionHandlingLock = new Object(); + private MediaPlayer mPlayer; + private PowerManager.WakeLock mWakeLock; + private AudioManager mAudioManager; + + // The current state according to the caller. Reality lags behind + // because of the asynchronous nature of this class. + private int mState = STOP; + + /** + * Construct a NotificationPlayer object. + * + * @param tag a string to use for debugging + */ + public NotificationPlayer(String tag) { + if (tag != null) { + mTag = tag; + } else { + mTag = "NotificationPlayer"; + } + } + + /** + * Start playing the sound. It will actually start playing at some + * point in the future. There are no guarantees about latency here. + * Calling this before another audio file is done playing will stop + * that one and start the new one. + * + * @param context Your application's context. + * @param uri The URI to play. (see {@link MediaPlayer#setDataSource(Context, Uri)}) + * @param looping Whether the audio should loop forever. + * (see {@link MediaPlayer#setLooping(boolean)}) + * @param stream the AudioStream to use. + * (see {@link MediaPlayer#setAudioStreamType(int)}) + */ + public void play(Context context, Uri uri, boolean looping, int stream) { + Command cmd = new Command(); + cmd.requestTime = SystemClock.uptimeMillis(); + cmd.code = PLAY; + cmd.context = context; + cmd.uri = uri; + cmd.looping = looping; + cmd.stream = stream; + synchronized (mCmdQueue) { + enqueueLocked(cmd); + mState = PLAY; + } + } + + /** + * Stop a previously played sound. It can't be played again or unpaused + * at this point. Calling this multiple times has no ill effects. + */ + public void stop() { + synchronized (mCmdQueue) { + // This check allows stop to be called multiple times without starting + // a thread that ends up doing nothing. + if (mState != STOP) { + Command cmd = new Command(); + cmd.requestTime = SystemClock.uptimeMillis(); + cmd.code = STOP; + enqueueLocked(cmd); + mState = STOP; + } + } + } + + private void enqueueLocked(Command cmd) { + mCmdQueue.add(cmd); + if (mThread == null) { + acquireWakeLock(); + mThread = new CmdThread(); + mThread.start(); + } + } + + /** + * We want to hold a wake lock while we do the prepare and play. The stop probably is + * optional, but it won't hurt to have it too. The problem is that if you start a sound + * while you're holding a wake lock (e.g. an alarm starting a notification), you want the + * sound to play, but if the CPU turns off before mThread gets to work, it won't. The + * simplest way to deal with this is to make it so there is a wake lock held while the + * thread is starting or running. You're going to need the WAKE_LOCK permission if you're + * going to call this. + * + * This must be called before the first time play is called. + * + * @hide + */ + public void setUsesWakeLock(Context context) { + if (mWakeLock != null || mThread != null) { + // if either of these has happened, we've already played something. + // and our releases will be out of sync. + throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock + + " mThread=" + mThread); + } + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag); + } + + private void acquireWakeLock() { + if (mWakeLock != null) { + mWakeLock.acquire(); + } + } + + private void releaseWakeLock() { + if (mWakeLock != null) { + mWakeLock.release(); + } + } +} + diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java index dbd1826..77bddb0 100644 --- a/services/java/com/android/server/PackageManagerBackupAgent.java +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -16,9 +16,9 @@ package com.android.server; -import android.app.BackupAgent; -import android.backup.BackupDataInput; -import android.backup.BackupDataOutput; +import android.app.backup.BackupAgent; +import android.app.backup.BackupDataInput; +import android.app.backup.BackupDataOutput; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -26,7 +26,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.os.Build; import android.os.ParcelFileDescriptor; -import android.util.Log; +import android.util.Slog; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Set; /** * We back up the signatures of each package so that during a system restore, @@ -94,21 +95,35 @@ public class PackageManagerBackupAgent extends BackupAgent { public Metadata getRestoredMetadata(String packageName) { if (mRestoredSignatures == null) { - Log.w(TAG, "getRestoredMetadata() before metadata read!"); + Slog.w(TAG, "getRestoredMetadata() before metadata read!"); return null; } return mRestoredSignatures.get(packageName); } + + public Set<String> getRestoredPackages() { + if (mRestoredSignatures == null) { + Slog.w(TAG, "getRestoredPackages() before metadata read!"); + return null; + } + + // This is technically the set of packages on the originating handset + // that had backup agents at all, not limited to the set of packages + // that had actually contributed a restore dataset, but it's a + // close enough approximation for our purposes and does not require any + // additional involvement by the transport to obtain. + return mRestoredSignatures.keySet(); + } // The backed up data is the signature block for each app, keyed by // the package name. public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { - if (DEBUG) Log.v(TAG, "onBackup()"); + if (DEBUG) Slog.v(TAG, "onBackup()"); - ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these - DataOutputStream outWriter = new DataOutputStream(bufStream); + ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream(); // we'll reuse these + DataOutputStream outputBufferStream = new DataOutputStream(outputBuffer); parseStateFile(oldState); // If the stored version string differs, we need to re-backup all @@ -116,7 +131,7 @@ public class PackageManagerBackupAgent extends BackupAgent { // "already backed up" map built by parseStateFile(). if (mStoredIncrementalVersion == null || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) { - Log.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs " + Slog.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs " + Build.VERSION.INCREMENTAL + " - rewriting"); mExisting.clear(); } @@ -132,14 +147,12 @@ public class PackageManagerBackupAgent extends BackupAgent { * the backup set. */ if (!mExisting.contains(GLOBAL_METADATA_KEY)) { - if (DEBUG) Log.v(TAG, "Storing global metadata key"); - outWriter.writeInt(Build.VERSION.SDK_INT); - outWriter.writeUTF(Build.VERSION.INCREMENTAL); - byte[] metadata = bufStream.toByteArray(); - data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length); - data.writeEntityData(metadata, metadata.length); + if (DEBUG) Slog.v(TAG, "Storing global metadata key"); + outputBufferStream.writeInt(Build.VERSION.SDK_INT); + outputBufferStream.writeUTF(Build.VERSION.INCREMENTAL); + writeEntity(data, GLOBAL_METADATA_KEY, outputBuffer.toByteArray()); } else { - if (DEBUG) Log.v(TAG, "Global metadata key already stored"); + if (DEBUG) Slog.v(TAG, "Global metadata key already stored"); // don't consider it to have been skipped/deleted mExisting.remove(GLOBAL_METADATA_KEY); } @@ -163,49 +176,46 @@ public class PackageManagerBackupAgent extends BackupAgent { continue; } - boolean doBackup = false; - if (!mExisting.contains(packName)) { - // We haven't backed up this app before - doBackup = true; - } else { - // We *have* backed this one up before. Check whether the version + if (mExisting.contains(packName)) { + // We have backed up this app before. Check whether the version // of the backup matches the version of the current app; if they // don't match, the app has been updated and we need to store its // metadata again. In either case, take it out of mExisting so that // we don't consider it deleted later. - if (info.versionCode != mStateVersions.get(packName).versionCode) { - doBackup = true; - } mExisting.remove(packName); + if (info.versionCode == mStateVersions.get(packName).versionCode) { + continue; + } + } + + if (info.signatures == null || info.signatures.length == 0) + { + Slog.w(TAG, "Not backing up package " + packName + + " since it appears to have no signatures."); + continue; } - if (doBackup) { - // We need to store this app's metadata - /* - * Metadata for each package: - * - * int version -- [4] the package's versionCode - * byte[] signatures -- [len] flattened Signature[] of the package - */ - - // marshal the version code in a canonical form - bufStream.reset(); - outWriter.writeInt(info.versionCode); - byte[] versionBuf = bufStream.toByteArray(); - - byte[] sigs = flattenSignatureArray(info.signatures); - - if (DEBUG) { - Log.v(TAG, "+ metadata for " + packName - + " version=" + info.versionCode - + " versionLen=" + versionBuf.length - + " sigsLen=" + sigs.length); - } - // Now we can write the backup entity for this package - data.writeEntityHeader(packName, versionBuf.length + sigs.length); - data.writeEntityData(versionBuf, versionBuf.length); - data.writeEntityData(sigs, sigs.length); + // We need to store this app's metadata + /* + * Metadata for each package: + * + * int version -- [4] the package's versionCode + * byte[] signatures -- [len] flattened Signature[] of the package + */ + + // marshal the version code in a canonical form + outputBuffer.reset(); + outputBufferStream.writeInt(info.versionCode); + writeSignatureArray(outputBufferStream, info.signatures); + + if (DEBUG) { + Slog.v(TAG, "+ writing metadata for " + packName + + " version=" + info.versionCode + + " entityLen=" + outputBuffer.size()); } + + // Now we can write the backup entity for this package + writeEntity(data, packName, outputBuffer.toByteArray()); } } @@ -213,23 +223,29 @@ public class PackageManagerBackupAgent extends BackupAgent { // mentioned in the saved state file, but appear to no longer be present // on the device. Write a deletion entity for them. for (String app : mExisting) { - if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app); + if (DEBUG) Slog.v(TAG, "- removing metadata for deleted pkg " + app); try { data.writeEntityHeader(app, -1); } catch (IOException e) { - Log.e(TAG, "Unable to write package deletions!"); + Slog.e(TAG, "Unable to write package deletions!"); return; } } } catch (IOException e) { // Real error writing data - Log.e(TAG, "Unable to write package backup data file!"); + Slog.e(TAG, "Unable to write package backup data file!"); return; } // Finally, write the new state blob -- just the list of all apps we handled writeStateFile(mAllPackages, newState); } + + private static void writeEntity(BackupDataOutput data, String key, byte[] bytes) + throws IOException { + data.writeEntityHeader(key, bytes.length); + data.writeEntityData(bytes, bytes.length); + } // "Restore" here is a misnomer. What we're really doing is reading back the // set of app signatures associated with each backed-up app in this restore @@ -238,47 +254,53 @@ public class PackageManagerBackupAgent extends BackupAgent { throws IOException { List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>(); - if (DEBUG) Log.v(TAG, "onRestore()"); + if (DEBUG) Slog.v(TAG, "onRestore()"); int storedSystemVersion = -1; while (data.readNextHeader()) { String key = data.getKey(); int dataSize = data.getDataSize(); - if (DEBUG) Log.v(TAG, " got key=" + key + " dataSize=" + dataSize); + if (DEBUG) Slog.v(TAG, " got key=" + key + " dataSize=" + dataSize); // generic setup to parse any entity data - byte[] dataBuf = new byte[dataSize]; - data.readEntityData(dataBuf, 0, dataSize); - ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); - DataInputStream in = new DataInputStream(baStream); + byte[] inputBytes = new byte[dataSize]; + data.readEntityData(inputBytes, 0, dataSize); + ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes); + DataInputStream inputBufferStream = new DataInputStream(inputBuffer); if (key.equals(GLOBAL_METADATA_KEY)) { - int storedSdkVersion = in.readInt(); - if (DEBUG) Log.v(TAG, " storedSystemVersion = " + storedSystemVersion); + int storedSdkVersion = inputBufferStream.readInt(); + if (DEBUG) Slog.v(TAG, " storedSystemVersion = " + storedSystemVersion); if (storedSystemVersion > Build.VERSION.SDK_INT) { // returning before setting the sig map means we rejected the restore set - Log.w(TAG, "Restore set was from a later version of Android; not restoring"); + Slog.w(TAG, "Restore set was from a later version of Android; not restoring"); return; } mStoredSdkVersion = storedSdkVersion; - mStoredIncrementalVersion = in.readUTF(); + mStoredIncrementalVersion = inputBufferStream.readUTF(); mHasMetadata = true; if (DEBUG) { - Log.i(TAG, "Restore set version " + storedSystemVersion + Slog.i(TAG, "Restore set version " + storedSystemVersion + " is compatible with OS version " + Build.VERSION.SDK_INT + " (" + mStoredIncrementalVersion + " vs " + Build.VERSION.INCREMENTAL + ")"); } } else { // it's a file metadata record - int versionCode = in.readInt(); - Signature[] sigs = unflattenSignatureArray(in); + int versionCode = inputBufferStream.readInt(); + Signature[] sigs = readSignatureArray(inputBufferStream); if (DEBUG) { - Log.i(TAG, " restored metadata for " + key + Slog.i(TAG, " read metadata for " + key + " dataSize=" + dataSize + " versionCode=" + versionCode + " sigs=" + sigs); } + + if (sigs == null || sigs.length == 0) { + Slog.w(TAG, "Not restoring package " + key + + " since it appears to have no signatures."); + continue; + } ApplicationInfo app = new ApplicationInfo(); app.packageName = key; @@ -291,63 +313,50 @@ public class PackageManagerBackupAgent extends BackupAgent { mRestoredSignatures = sigMap; } - - // Util: convert an array of Signatures into a flattened byte buffer. The - // flattened format contains enough info to reconstruct the signature array. - private byte[] flattenSignatureArray(Signature[] allSigs) { - ByteArrayOutputStream outBuf = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(outBuf); - - // build the set of subsidiary buffers - try { - // first the # of signatures in the array - out.writeInt(allSigs.length); - - // then the signatures themselves, length + flattened buffer - for (Signature sig : allSigs) { - byte[] flat = sig.toByteArray(); - out.writeInt(flat.length); - out.write(flat); - } - } catch (IOException e) { - // very strange; we're writing to memory here. abort. - return null; + private static void writeSignatureArray(DataOutputStream out, Signature[] sigs) + throws IOException { + // write the number of signatures in the array + out.writeInt(sigs.length); + + // write the signatures themselves, length + flattened buffer + for (Signature sig : sigs) { + byte[] flat = sig.toByteArray(); + out.writeInt(flat.length); + out.write(flat); } - - return outBuf.toByteArray(); } - private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) { - Signature[] sigs = null; - + private static Signature[] readSignatureArray(DataInputStream in) { try { - int num = in.readInt(); - if (DEBUG) Log.v(TAG, " ... unflatten read " + num); - + int num; + try { + num = in.readInt(); + } catch (EOFException e) { + // clean termination + Slog.w(TAG, "Read empty signature block"); + return null; + } + + if (DEBUG) Slog.v(TAG, " ... unflatten read " + num); + // Sensical? if (num > 20) { - Log.e(TAG, "Suspiciously large sig count in restore data; aborting"); + Slog.e(TAG, "Suspiciously large sig count in restore data; aborting"); throw new IllegalStateException("Bad restore state"); } - - sigs = new Signature[num]; + + Signature[] sigs = new Signature[num]; for (int i = 0; i < num; i++) { int len = in.readInt(); byte[] flatSig = new byte[len]; in.read(flatSig); sigs[i] = new Signature(flatSig); } - } catch (EOFException e) { - // clean termination - if (sigs == null) { - Log.w(TAG, "Empty signature block found"); - } + return sigs; } catch (IOException e) { - Log.e(TAG, "Unable to unflatten sigs"); + Slog.e(TAG, "Unable to read signatures"); return null; } - - return sigs; } // Util: parse out an existing state file into a usable structure @@ -373,7 +382,7 @@ public class PackageManagerBackupAgent extends BackupAgent { mStoredIncrementalVersion = in.readUTF(); mExisting.add(GLOBAL_METADATA_KEY); } else { - Log.e(TAG, "No global metadata in state file!"); + Slog.e(TAG, "No global metadata in state file!"); return; } @@ -388,7 +397,7 @@ public class PackageManagerBackupAgent extends BackupAgent { // safe; we're done } catch (IOException e) { // whoops, bad state file. abort. - Log.e(TAG, "Unable to read Package Manager state file: " + e); + Slog.e(TAG, "Unable to read Package Manager state file: " + e); } } @@ -409,7 +418,7 @@ public class PackageManagerBackupAgent extends BackupAgent { out.writeInt(pkg.versionCode); } } catch (IOException e) { - Log.e(TAG, "Unable to write package manager state file!"); + Slog.e(TAG, "Unable to write package manager state file!"); return; } } diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index cc78300..6aa1cbf 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -16,8 +16,11 @@ package com.android.server; +import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; +import com.android.internal.content.PackageHelper; import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -26,11 +29,15 @@ import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManagerNative; import android.app.IActivityManager; -import android.content.ComponentName; +import android.app.admin.IDevicePolicyManager; +import android.app.backup.IBackupManager; import android.content.Context; +import android.content.ComponentName; +import android.content.IIntentReceiver; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; +import android.content.ServiceConnection; import android.content.IntentSender.SendIntentException; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -40,16 +47,16 @@ import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageManager; +import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageStats; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; -import static android.content.pm.PackageManager.PKG_INSTALL_COMPLETE; -import static android.content.pm.PackageManager.PKG_INSTALL_INCOMPLETE; import android.content.pm.PackageParser; import android.content.pm.PermissionInfo; import android.content.pm.PermissionGroupInfo; @@ -61,7 +68,9 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.Debug; import android.os.HandlerThread; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; @@ -75,6 +84,8 @@ import android.os.Process; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.provider.Settings; +import android.security.SystemKeyStore; import android.util.*; import android.view.Display; import android.view.WindowManager; @@ -89,11 +100,14 @@ import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -106,10 +120,24 @@ import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; +/** + * Keep track of all those .apks everywhere. + * + * This is very central to the platform's security; please run the unit + * tests whenever making modifications here: + * +mmm frameworks/base/tests/AndroidTests +adb install -r -f out/target/product/passion/data/app/AndroidTests.apk +adb shell am instrument -w -e class com.android.unit_tests.PackageManagerTests com.android.unit_tests/android.test.InstrumentationTestRunner + * + */ class PackageManagerService extends IPackageManager.Stub { private static final String TAG = "PackageManager"; private static final boolean DEBUG_SETTINGS = false; private static final boolean DEBUG_PREFERRED = false; + private static final boolean DEBUG_UPGRADE = false; + private static final boolean DEBUG_INSTALL = false; + private static final boolean DEBUG_NATIVE = false; private static final boolean MULTIPLE_APPLICATION_UIDS = true; private static final int RADIO_UID = Process.PHONE_UID; @@ -128,20 +156,36 @@ class PackageManagerService extends IPackageManager.Stub { FileObserver.CLOSE_WRITE /*| FileObserver.CREATE*/ | FileObserver.MOVED_TO; private static final int OBSERVER_EVENTS = REMOVE_EVENTS | ADD_EVENTS; + // Suffix used during package installation when copying/moving + // package apks to install directory. + private static final String INSTALL_PACKAGE_SUFFIX = "-"; + + /** + * Indicates the state of installation. Used by PackageManager to + * figure out incomplete installations. Say a package is being installed + * (the state is set to PKG_INSTALL_INCOMPLETE) and remains so till + * the package installation is successful or unsuccesful lin which case + * the PackageManager will no longer maintain state information associated + * with the package. If some exception(like device freeze or battery being + * pulled out) occurs during installation of a package, the PackageManager + * needs this information to clean up the previously failed installation. + */ + private static final int PKG_INSTALL_INCOMPLETE = 0; + private static final int PKG_INSTALL_COMPLETE = 1; static final int SCAN_MONITOR = 1<<0; static final int SCAN_NO_DEX = 1<<1; static final int SCAN_FORCE_DEX = 1<<2; static final int SCAN_UPDATE_SIGNATURE = 1<<3; - static final int SCAN_FORWARD_LOCKED = 1<<4; - static final int SCAN_NEW_INSTALL = 1<<5; - - static final int LOG_BOOT_PROGRESS_PMS_START = 3060; - static final int LOG_BOOT_PROGRESS_PMS_SYSTEM_SCAN_START = 3070; - static final int LOG_BOOT_PROGRESS_PMS_DATA_SCAN_START = 3080; - static final int LOG_BOOT_PROGRESS_PMS_SCAN_END = 3090; - static final int LOG_BOOT_PROGRESS_PMS_READY = 3100; + static final int SCAN_NEW_INSTALL = 1<<4; + static final int SCAN_NO_PATHS = 1<<5; + static final int REMOVE_CHATTY = 1<<16; + + static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( + "com.android.defcontainer", + "com.android.defcontainer.DefaultContainerService"); + final HandlerThread mHandlerThread = new HandlerThread("PackageManager", Process.THREAD_PRIORITY_BACKGROUND); final PackageHandler mHandler; @@ -149,7 +193,7 @@ class PackageManagerService extends IPackageManager.Stub { final int mSdkVersion = Build.VERSION.SDK_INT; final String mSdkCodename = "REL".equals(Build.VERSION.CODENAME) ? null : Build.VERSION.CODENAME; - + final Context mContext; final boolean mFactoryTest; final boolean mNoDexOpt; @@ -175,7 +219,7 @@ class PackageManagerService extends IPackageManager.Stub { // Used for priviledge escalation. MUST NOT BE CALLED WITH mPackages // LOCK HELD. Can be called with mInstallLock held. final Installer mInstaller; - + final File mFrameworkDir; final File mSystemAppDir; final File mAppInstallDir; @@ -184,14 +228,14 @@ class PackageManagerService extends IPackageManager.Stub { // Directory containing the private parts (e.g. code and non-resource assets) of forward-locked // apps. final File mDrmAppPrivateInstallDir; - + // ---------------------------------------------------------------- - + // Lock for state used when installing and doing other long running // operations. Methods that must be called with this lock held have // the prefix "LI". final Object mInstallLock = new Object(); - + // These are the directories in the 3rd party applications installed dir // that we have currently loaded packages from. Keys are the application's // installed zip file (absolute codePath), and values are Package. @@ -205,7 +249,7 @@ class PackageManagerService extends IPackageManager.Stub { final int[] mOutPermissions = new int[3]; // ---------------------------------------------------------------- - + // Keys are String (package name), values are Package. This also serves // as the lock for the global state. Methods that must be called with // this lock held have the prefix "LP". @@ -214,7 +258,6 @@ class PackageManagerService extends IPackageManager.Stub { final Settings mSettings; boolean mRestoredSettings; - boolean mReportedUidError; // Group-ids that are given to all packages as read from etc/permissions/*.xml. int[] mGlobalGids; @@ -223,19 +266,19 @@ class PackageManagerService extends IPackageManager.Stub { // etc/permissions.xml file. final SparseArray<HashSet<String>> mSystemPermissions = new SparseArray<HashSet<String>>(); - + // These are the built-in shared libraries that were read from the // etc/permissions.xml file. final HashMap<String, String> mSharedLibraries = new HashMap<String, String>(); - + // Temporary for building the final shared libraries for an .apk. String[] mTmpSharedLibraries = null; - + // These are the features this devices supports that were read from the // etc/permissions.xml file. final HashMap<String, FeatureInfo> mAvailableFeatures = new HashMap<String, FeatureInfo>(); - + // All available activities, for your resolving pleasure. final ActivityIntentResolver mActivities = new ActivityIntentResolver(); @@ -264,9 +307,13 @@ class PackageManagerService extends IPackageManager.Stub { final HashMap<String, PackageParser.PermissionGroup> mPermissionGroups = new HashMap<String, PackageParser.PermissionGroup>(); + // Packages whose data we have transfered into another package, thus + // should no longer exist. + final HashSet<String> mTransferedPackages = new HashSet<String>(); + // Broadcast actions that are only available to the system. final HashSet<String> mProtectedBroadcasts = new HashSet<String>(); - + boolean mSystemReady; boolean mSafeMode; boolean mHasSystemUidErrors; @@ -280,21 +327,201 @@ class PackageManagerService extends IPackageManager.Stub { // Set of pending broadcasts for aggregating enable/disable of components. final HashMap<String, ArrayList<String>> mPendingBroadcasts = new HashMap<String, ArrayList<String>>(); + // Service Connection to remote media container service to copy + // package uri's from external media onto secure containers + // or internal storage. + private IMediaContainerService mContainerService = null; + static final int SEND_PENDING_BROADCAST = 1; + static final int MCS_BOUND = 3; + static final int END_COPY = 4; + static final int INIT_COPY = 5; + static final int MCS_UNBIND = 6; + static final int START_CLEANING_PACKAGE = 7; + static final int FIND_INSTALL_LOC = 8; + static final int POST_INSTALL = 9; + static final int MCS_RECONNECT = 10; + static final int MCS_GIVE_UP = 11; + static final int UPDATED_MEDIA_STATUS = 12; + static final int WRITE_SETTINGS = 13; + + static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds + // Delay time in millisecs static final int BROADCAST_DELAY = 10 * 1000; + final private DefaultContainerConnection mDefContainerConn = + new DefaultContainerConnection(); + class DefaultContainerConnection implements ServiceConnection { + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected"); + IMediaContainerService imcs = + IMediaContainerService.Stub.asInterface(service); + mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); + } + + public void onServiceDisconnected(ComponentName name) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected"); + } + }; + + // Recordkeeping of restore-after-install operations that are currently in flight + // between the Package Manager and the Backup Manager + class PostInstallData { + public InstallArgs args; + public PackageInstalledInfo res; + + PostInstallData(InstallArgs _a, PackageInstalledInfo _r) { + args = _a; + res = _r; + } + }; + final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<PostInstallData>(); + int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows class PackageHandler extends Handler { + private boolean mBound = false; + final ArrayList<HandlerParams> mPendingInstalls = + new ArrayList<HandlerParams>(); + + private boolean connectToService() { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" + + " DefaultContainerService"); + Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + if (mContext.bindService(service, mDefContainerConn, + Context.BIND_AUTO_CREATE)) { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + mBound = true; + return true; + } + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + return false; + } + + private void disconnectService() { + mContainerService = null; + mBound = false; + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + mContext.unbindService(mDefContainerConn); + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + } + PackageHandler(Looper looper) { super(looper); } + public void handleMessage(Message msg) { + try { + doHandleMessage(msg); + } finally { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + } + } + + void doHandleMessage(Message msg) { switch (msg.what) { + case INIT_COPY: { + if (DEBUG_SD_INSTALL) Log.i(TAG, "init_copy"); + HandlerParams params = (HandlerParams) msg.obj; + int idx = mPendingInstalls.size(); + if (DEBUG_SD_INSTALL) Log.i(TAG, "idx=" + idx); + // If a bind was already initiated we dont really + // need to do anything. The pending install + // will be processed later on. + if (!mBound) { + // If this is the only one pending we might + // have to bind to the service again. + if (!connectToService()) { + Slog.e(TAG, "Failed to bind to media container service"); + params.serviceError(); + return; + } else { + // Once we bind to the service, the first + // pending request will be processed. + mPendingInstalls.add(idx, params); + } + } else { + mPendingInstalls.add(idx, params); + // Already bound to the service. Just make + // sure we trigger off processing the first request. + if (idx == 0) { + mHandler.sendEmptyMessage(MCS_BOUND); + } + } + break; + } + case MCS_BOUND: { + if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_bound"); + if (msg.obj != null) { + mContainerService = (IMediaContainerService) msg.obj; + } + if (mContainerService == null) { + // Something seriously wrong. Bail out + Slog.e(TAG, "Cannot bind to media container service"); + for (HandlerParams params : mPendingInstalls) { + mPendingInstalls.remove(0); + // Indicate service bind error + params.serviceError(); + } + mPendingInstalls.clear(); + } else if (mPendingInstalls.size() > 0) { + HandlerParams params = mPendingInstalls.get(0); + if (params != null) { + params.startCopy(); + } + } else { + // Should never happen ideally. + Slog.w(TAG, "Empty queue"); + } + break; + } + case MCS_RECONNECT : { + if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_reconnect"); + if (mPendingInstalls.size() > 0) { + if (mBound) { + disconnectService(); + } + if (!connectToService()) { + Slog.e(TAG, "Failed to bind to media container service"); + for (HandlerParams params : mPendingInstalls) { + mPendingInstalls.remove(0); + // Indicate service bind error + params.serviceError(); + } + mPendingInstalls.clear(); + } + } + break; + } + case MCS_UNBIND : { + if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_unbind"); + // Delete pending install + if (mPendingInstalls.size() > 0) { + mPendingInstalls.remove(0); + } + if (mPendingInstalls.size() == 0) { + if (mBound) { + disconnectService(); + } + } else { + // There are more pending requests in queue. + // Just post MCS_BOUND message to trigger processing + // of next pending install. + mHandler.sendEmptyMessage(MCS_BOUND); + } + break; + } + case MCS_GIVE_UP: { + if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_giveup too many retries"); + HandlerParams params = mPendingInstalls.remove(0); + break; + } case SEND_PENDING_BROADCAST : { String packages[]; ArrayList components[]; int size = 0; int uids[]; + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPackages) { if (mPendingBroadcasts == null) { return; @@ -326,11 +553,130 @@ class PackageManagerService extends IPackageManager.Stub { sendPackageChangedBroadcast(packages[i], true, (ArrayList<String>)components[i], uids[i]); } + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); break; } + case START_CLEANING_PACKAGE: { + String packageName = (String)msg.obj; + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + synchronized (mPackages) { + if (!mSettings.mPackagesToBeCleaned.contains(packageName)) { + mSettings.mPackagesToBeCleaned.add(packageName); + } + } + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + startCleaningPackages(); + } break; + case POST_INSTALL: { + if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1); + PostInstallData data = mRunningInstalls.get(msg.arg1); + mRunningInstalls.delete(msg.arg1); + boolean deleteOld = false; + + if (data != null) { + InstallArgs args = data.args; + PackageInstalledInfo res = data.res; + + if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { + res.removedInfo.sendBroadcast(false, true); + Bundle extras = new Bundle(1); + extras.putInt(Intent.EXTRA_UID, res.uid); + final boolean update = res.removedInfo.removedPackage != null; + if (update) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, + res.pkg.applicationInfo.packageName, + extras, null); + if (update) { + sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, + res.pkg.applicationInfo.packageName, + extras, null); + } + if (res.removedInfo.args != null) { + // Remove the replaced package's older resources safely now + deleteOld = true; + } + } + // Force a gc to clear up things + Runtime.getRuntime().gc(); + // We delete after a gc for applications on sdcard. + if (deleteOld) { + synchronized (mInstallLock) { + res.removedInfo.args.doPostDeleteLI(true); + } + } + if (args.observer != null) { + try { + args.observer.packageInstalled(res.name, res.returnCode); + } catch (RemoteException e) { + Slog.i(TAG, "Observer no longer exists."); + } + } + } else { + Slog.e(TAG, "Bogus post-install token " + msg.arg1); + } + } break; + case UPDATED_MEDIA_STATUS: { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Got message UPDATED_MEDIA_STATUS"); + boolean reportStatus = msg.arg1 == 1; + boolean doGc = msg.arg2 == 1; + if (DEBUG_SD_INSTALL) Log.i(TAG, "reportStatus=" + reportStatus + ", doGc = " + doGc); + if (doGc) { + // Force a gc to clear up stale containers. + Runtime.getRuntime().gc(); + } + if (msg.obj != null) { + Set<SdInstallArgs> args = (Set<SdInstallArgs>) msg.obj; + if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading all containers"); + // Unload containers + unloadAllContainers(args); + } + if (reportStatus) { + try { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Invoking MountService call back"); + PackageHelper.getMountService().finishMediaUpdate(); + } catch (RemoteException e) { + Log.e(TAG, "MountService not running?"); + } + } + } break; + case WRITE_SETTINGS: { + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + synchronized (mPackages) { + removeMessages(WRITE_SETTINGS); + mSettings.writeLP(); + } + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + } break; } } } + + void scheduleWriteSettingsLocked() { + if (!mHandler.hasMessages(WRITE_SETTINGS)) { + mHandler.sendEmptyMessageDelayed(WRITE_SETTINGS, WRITE_SETTINGS_DELAY); + } + } + + static boolean installOnSd(int flags) { + if (((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) || + ((flags & PackageManager.INSTALL_INTERNAL) != 0)) { + return false; + } + if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) { + return true; + } + return false; + } + + static boolean isFwdLocked(int flags) { + if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) { + return true; + } + return false; + } + public static final IPackageManager main(Context context, boolean factoryTest) { PackageManagerService m = new PackageManagerService(context, factoryTest); ServiceManager.addService("package", m); @@ -344,7 +690,7 @@ class PackageManagerService extends IPackageManager.Stub { count++; i++; } - + String[] res = new String[count]; i=0; count = 0; @@ -358,15 +704,15 @@ class PackageManagerService extends IPackageManager.Stub { res[count] = str.substring(lastI, str.length()); return res; } - + public PackageManagerService(Context context, boolean factoryTest) { - EventLog.writeEvent(LOG_BOOT_PROGRESS_PMS_START, + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); - + if (mSdkVersion <= 0) { - Log.w(TAG, "**** ro.build.version.sdk not set!"); + Slog.w(TAG, "**** ro.build.version.sdk not set!"); } - + mContext = context; mFactoryTest = factoryTest; mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); @@ -388,18 +734,18 @@ class PackageManagerService extends IPackageManager.Stub { if ("*".equals(separateProcesses)) { mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES; mSeparateProcesses = null; - Log.w(TAG, "Running with debug.separate_processes: * (ALL)"); + Slog.w(TAG, "Running with debug.separate_processes: * (ALL)"); } else { mDefParseFlags = 0; mSeparateProcesses = separateProcesses.split(","); - Log.w(TAG, "Running with debug.separate_processes: " + Slog.w(TAG, "Running with debug.separate_processes: " + separateProcesses); } } else { mDefParseFlags = 0; mSeparateProcesses = null; } - + Installer installer = new Installer(); // Little hacky thing to check if installd is here, to determine // whether we are running on the simulator and thus need to take @@ -419,7 +765,7 @@ class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { mHandlerThread.start(); mHandler = new PackageHandler(mHandlerThread.getLooper()); - + File dataDir = Environment.getDataDirectory(); mAppDataDir = new File(dataDir, "data"); mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); @@ -438,24 +784,26 @@ class PackageManagerService extends IPackageManager.Stub { mRestoredSettings = mSettings.readLP(); long startTime = SystemClock.uptimeMillis(); - - EventLog.writeEvent(LOG_BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, + + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime); - - int scanMode = SCAN_MONITOR; + + // Set flag to monitor and not change apk file paths when + // scanning install directories. + int scanMode = SCAN_MONITOR | SCAN_NO_PATHS; if (mNoDexOpt) { - Log.w(TAG, "Running ENG build: no pre-dexopt!"); - scanMode |= SCAN_NO_DEX; + Slog.w(TAG, "Running ENG build: no pre-dexopt!"); + scanMode |= SCAN_NO_DEX; } - + final HashSet<String> libFiles = new HashSet<String>(); - + mFrameworkDir = new File(Environment.getRootDirectory(), "framework"); mDalvikCacheDir = new File(dataDir, "dalvik-cache"); - + if (mInstaller != null) { boolean didDexOpt = false; - + /** * Out of paranoia, ensure that everything in the boot class * path has been dexed. @@ -471,15 +819,15 @@ class PackageManagerService extends IPackageManager.Stub { didDexOpt = true; } } catch (FileNotFoundException e) { - Log.w(TAG, "Boot class path not found: " + paths[i]); + Slog.w(TAG, "Boot class path not found: " + paths[i]); } catch (IOException e) { - Log.w(TAG, "Exception reading boot class path: " + paths[i], e); + Slog.w(TAG, "Exception reading boot class path: " + paths[i], e); } } } else { - Log.w(TAG, "No BOOTCLASSPATH found!"); + Slog.w(TAG, "No BOOTCLASSPATH found!"); } - + /** * Also ensure all external libraries have had dexopt run on them. */ @@ -494,17 +842,17 @@ class PackageManagerService extends IPackageManager.Stub { didDexOpt = true; } } catch (FileNotFoundException e) { - Log.w(TAG, "Library not found: " + lib); + Slog.w(TAG, "Library not found: " + lib); } catch (IOException e) { - Log.w(TAG, "Exception reading library: " + lib, e); + Slog.w(TAG, "Exception reading library: " + lib, e); } } } - + // Gross hack for now: we know this file doesn't contain any // code, so don't dexopt it to avoid the resulting log spew. libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk"); - + /** * And there are a number of commands implemented in Java, which * we currently need to do the dexopt on so that they can be @@ -529,13 +877,13 @@ class PackageManagerService extends IPackageManager.Stub { didDexOpt = true; } } catch (FileNotFoundException e) { - Log.w(TAG, "Jar not found: " + path); + Slog.w(TAG, "Jar not found: " + path); } catch (IOException e) { - Log.w(TAG, "Exception reading jar: " + path, e); + Slog.w(TAG, "Exception reading jar: " + path, e); } } } - + if (didDexOpt) { // If we had to do a dexopt of one of the previous // things, then something on the system has changed. @@ -548,24 +896,52 @@ class PackageManagerService extends IPackageManager.Stub { String fn = files[i]; if (fn.startsWith("data@app@") || fn.startsWith("data@app-private@")) { - Log.i(TAG, "Pruning dalvik file: " + fn); + Slog.i(TAG, "Pruning dalvik file: " + fn); (new File(mDalvikCacheDir, fn)).delete(); } } } } } - + + // Find base frameworks (resource packages without code). mFrameworkInstallObserver = new AppDirObserver( mFrameworkDir.getPath(), OBSERVER_EVENTS, true); mFrameworkInstallObserver.startWatching(); - scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM, + scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM + | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_NO_DEX); + + // Collect all system packages. mSystemAppDir = new File(Environment.getRootDirectory(), "app"); mSystemInstallObserver = new AppDirObserver( mSystemAppDir.getPath(), OBSERVER_EVENTS, true); mSystemInstallObserver.startWatching(); - scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode); + scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM + | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode); + + if (mInstaller != null) { + if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands"); + mInstaller.moveFiles(); + } + + // Prune any system packages that no longer exist. + Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); + while (psit.hasNext()) { + PackageSetting ps = psit.next(); + if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0 + && !mPackages.containsKey(ps.name) + && !mSettings.mDisabledSysPackages.containsKey(ps.name)) { + psit.remove(); + String msg = "System package " + ps.name + + " no longer exists; wiping its data"; + reportSettingsProblem(Log.WARN, msg); + if (mInstaller != null) { + mInstaller.remove(ps.name); + } + } + } + mAppInstallDir = new File(dataDir, "app"); if (mInstaller == null) { // Make sure these dirs exist, when we are running in @@ -581,8 +957,8 @@ class PackageManagerService extends IPackageManager.Stub { } //delete tmp files deleteTempPackageFiles(); - - EventLog.writeEvent(LOG_BOOT_PROGRESS_PMS_DATA_SCAN_START, + + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); mAppInstallObserver = new AppDirObserver( mAppInstallDir.getPath(), OBSERVER_EVENTS, false); @@ -592,21 +968,34 @@ class PackageManagerService extends IPackageManager.Stub { mDrmAppInstallObserver = new AppDirObserver( mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false); mDrmAppInstallObserver.startWatching(); - scanDirLI(mDrmAppPrivateInstallDir, 0, scanMode | SCAN_FORWARD_LOCKED); + scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, scanMode); - EventLog.writeEvent(LOG_BOOT_PROGRESS_PMS_SCAN_END, + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis()); - Log.i(TAG, "Time to scan packages: " + Slog.i(TAG, "Time to scan packages: " + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); - updatePermissionsLP(); + // If the platform SDK has changed since the last time we booted, + // we need to re-grant app permission to catch any new ones that + // appear. This is really a hack, and means that apps can in some + // cases get permissions that the user didn't initially explicitly + // allow... it would be nice to have some better way to handle + // this situation. + final boolean regrantPermissions = mSettings.mInternalSdkPlatform + != mSdkVersion; + if (regrantPermissions) Slog.i(TAG, "Platform changed from " + + mSettings.mInternalSdkPlatform + " to " + mSdkVersion + + "; regranting permissions for internal storage"); + mSettings.mInternalSdkPlatform = mSdkVersion; + + updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions); mSettings.writeLP(); - EventLog.writeEvent(LOG_BOOT_PROGRESS_PMS_READY, + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); - + // Now after opening every single application zip, make sure they // are all flushed. Not really needed, but keeps things nice and // tidy. @@ -622,18 +1011,18 @@ class PackageManagerService extends IPackageManager.Stub { return super.onTransact(code, data, reply, flags); } catch (RuntimeException e) { if (!(e instanceof SecurityException) && !(e instanceof IllegalArgumentException)) { - Log.e(TAG, "Package Manager Crash", e); + Slog.e(TAG, "Package Manager Crash", e); } throw e; } } void cleanupInstallFailedPackage(PackageSetting ps) { - Log.i(TAG, "Cleaning up incompletely installed app: " + ps.name); + Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name); if (mInstaller != null) { int retCode = mInstaller.remove(ps.name); if (retCode < 0) { - Log.w(TAG, "Couldn't remove app data directory for package: " + Slog.w(TAG, "Couldn't remove app data directory for package: " + ps.name + ", retcode=" + retCode); } } else { @@ -644,12 +1033,12 @@ class PackageManagerService extends IPackageManager.Stub { } if (ps.codePath != null) { if (!ps.codePath.delete()) { - Log.w(TAG, "Unable to remove old code file: " + ps.codePath); + Slog.w(TAG, "Unable to remove old code file: " + ps.codePath); } } if (ps.resourcePath != null) { if (!ps.resourcePath.delete() && !ps.resourcePath.equals(ps.codePath)) { - Log.w(TAG, "Unable to remove old code file: " + ps.resourcePath); + Slog.w(TAG, "Unable to remove old code file: " + ps.resourcePath); } } mSettings.removePackageLP(ps.name); @@ -659,11 +1048,11 @@ class PackageManagerService extends IPackageManager.Stub { // Read permissions from .../etc/permission directory. File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions"); if (!libraryDir.exists() || !libraryDir.isDirectory()) { - Log.w(TAG, "No directory " + libraryDir + ", skipping"); + Slog.w(TAG, "No directory " + libraryDir + ", skipping"); return; } if (!libraryDir.canRead()) { - Log.w(TAG, "Directory " + libraryDir + " cannot be read"); + Slog.w(TAG, "Directory " + libraryDir + " cannot be read"); return; } @@ -673,24 +1062,24 @@ class PackageManagerService extends IPackageManager.Stub { if (f.getPath().endsWith("etc/permissions/platform.xml")) { continue; } - + if (!f.getPath().endsWith(".xml")) { - Log.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); + Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); continue; } if (!f.canRead()) { - Log.w(TAG, "Permissions library file " + f + " cannot be read"); + Slog.w(TAG, "Permissions library file " + f + " cannot be read"); continue; } readPermissionsFromXml(f); } - + // Read permissions from .../etc/permissions/platform.xml last so it will take precedence final File permFile = new File(Environment.getRootDirectory(), "etc/permissions/platform.xml"); readPermissionsFromXml(permFile); - + StringBuilder sb = new StringBuilder(128); sb.append("Libs:"); Iterator<String> it = mSharedLibraries.keySet().iterator(); @@ -702,7 +1091,7 @@ class PackageManagerService extends IPackageManager.Stub { sb.append(mSharedLibraries.get(name)); } Log.i(TAG, sb.toString()); - + sb.setLength(0); sb.append("Features:"); it = mAvailableFeatures.keySet().iterator(); @@ -712,13 +1101,13 @@ class PackageManagerService extends IPackageManager.Stub { } Log.i(TAG, sb.toString()); } - - private void readPermissionsFromXml(File permFile) { + + private void readPermissionsFromXml(File permFile) { FileReader permReader = null; try { permReader = new FileReader(permFile); } catch (FileNotFoundException e) { - Log.w(TAG, "Couldn't find or open permissions file " + permFile); + Slog.w(TAG, "Couldn't find or open permissions file " + permFile); return; } @@ -741,7 +1130,7 @@ class PackageManagerService extends IPackageManager.Stub { int gid = Integer.parseInt(gidStr); mGlobalGids = appendInt(mGlobalGids, gid); } else { - Log.w(TAG, "<group> without gid at " + Slog.w(TAG, "<group> without gid at " + parser.getPositionDescription()); } @@ -750,32 +1139,32 @@ class PackageManagerService extends IPackageManager.Stub { } else if ("permission".equals(name)) { String perm = parser.getAttributeValue(null, "name"); if (perm == null) { - Log.w(TAG, "<permission> without name at " + Slog.w(TAG, "<permission> without name at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } perm = perm.intern(); readPermission(parser, perm); - + } else if ("assign-permission".equals(name)) { String perm = parser.getAttributeValue(null, "name"); if (perm == null) { - Log.w(TAG, "<assign-permission> without name at " + Slog.w(TAG, "<assign-permission> without name at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } String uidStr = parser.getAttributeValue(null, "uid"); if (uidStr == null) { - Log.w(TAG, "<assign-permission> without uid at " + Slog.w(TAG, "<assign-permission> without uid at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } int uid = Process.getUidForName(uidStr); if (uid < 0) { - Log.w(TAG, "<assign-permission> with unknown uid \"" + Slog.w(TAG, "<assign-permission> with unknown uid \"" + uidStr + "\" at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); @@ -789,15 +1178,15 @@ class PackageManagerService extends IPackageManager.Stub { } perms.add(perm); XmlUtils.skipCurrentTag(parser); - + } else if ("library".equals(name)) { String lname = parser.getAttributeValue(null, "name"); String lfile = parser.getAttributeValue(null, "file"); if (lname == null) { - Log.w(TAG, "<library> without name at " + Slog.w(TAG, "<library> without name at " + parser.getPositionDescription()); } else if (lfile == null) { - Log.w(TAG, "<library> without file at " + Slog.w(TAG, "<library> without file at " + parser.getPositionDescription()); } else { //Log.i(TAG, "Got library " + lname + " in " + lfile); @@ -805,11 +1194,11 @@ class PackageManagerService extends IPackageManager.Stub { } XmlUtils.skipCurrentTag(parser); continue; - + } else if ("feature".equals(name)) { String fname = parser.getAttributeValue(null, "name"); if (fname == null) { - Log.w(TAG, "<feature> without name at " + Slog.w(TAG, "<feature> without name at " + parser.getPositionDescription()); } else { //Log.i(TAG, "Got feature " + fname); @@ -819,7 +1208,7 @@ class PackageManagerService extends IPackageManager.Stub { } XmlUtils.skipCurrentTag(parser); continue; - + } else { XmlUtils.skipCurrentTag(parser); continue; @@ -827,9 +1216,9 @@ class PackageManagerService extends IPackageManager.Stub { } } catch (XmlPullParserException e) { - Log.w(TAG, "Got execption parsing permissions.", e); + Slog.w(TAG, "Got execption parsing permissions.", e); } catch (IOException e) { - Log.w(TAG, "Got execption parsing permissions.", e); + Slog.w(TAG, "Got execption parsing permissions.", e); } } @@ -860,7 +1249,7 @@ class PackageManagerService extends IPackageManager.Stub { int gid = Process.getGidForName(gidStr); bp.gids = appendInt(bp.gids, gid); } else { - Log.w(TAG, "<group> without gid at " + Slog.w(TAG, "<group> without gid at " + parser.getPositionDescription()); } } @@ -894,6 +1283,36 @@ class PackageManagerService extends IPackageManager.Stub { return cur; } + static int[] removeInt(int[] cur, int val) { + if (cur == null) { + return null; + } + final int N = cur.length; + for (int i=0; i<N; i++) { + if (cur[i] == val) { + int[] ret = new int[N-1]; + if (i > 0) { + System.arraycopy(cur, 0, ret, 0, i); + } + if (i < (N-1)) { + System.arraycopy(cur, i + 1, ret, i, N - i - 1); + } + return ret; + } + } + return cur; + } + + static int[] removeInts(int[] cur, int[] rem) { + if (rem == null) return cur; + if (cur == null) return cur; + final int N = rem.length; + for (int i=0; i<N; i++) { + cur = removeInt(cur, rem[i]); + } + return cur; + } + PackageInfo generatePackageInfo(PackageParser.Package p, int flags) { if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { // The package has been uninstalled but has retained data and resources. @@ -923,6 +1342,28 @@ class PackageManagerService extends IPackageManager.Stub { return null; } + public String[] currentToCanonicalPackageNames(String[] names) { + String[] out = new String[names.length]; + synchronized (mPackages) { + for (int i=names.length-1; i>=0; i--) { + PackageSetting ps = mSettings.mPackages.get(names[i]); + out[i] = ps != null && ps.realName != null ? ps.realName : names[i]; + } + } + return out; + } + + public String[] canonicalToCurrentPackageNames(String[] names) { + String[] out = new String[names.length]; + synchronized (mPackages) { + for (int i=names.length-1; i>=0; i--) { + String cur = mSettings.mRenamedPackages.get(names[i]); + out[i] = cur != null ? cur : names[i]; + } + } + return out; + } + public int getPackageUid(String packageName) { synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); @@ -954,11 +1395,24 @@ class PackageManagerService extends IPackageManager.Stub { return new int[0]; } + static final PermissionInfo generatePermissionInfo( + BasePermission bp, int flags) { + if (bp.perm != null) { + return PackageParser.generatePermissionInfo(bp.perm, flags); + } + PermissionInfo pi = new PermissionInfo(); + pi.name = bp.name; + pi.packageName = bp.sourcePackage; + pi.nonLocalizedLabel = bp.name; + pi.protectionLevel = bp.protectionLevel; + return pi; + } + public PermissionInfo getPermissionInfo(String name, int flags) { synchronized (mPackages) { final BasePermission p = mSettings.mPermissions.get(name); - if (p != null && p.perm != null) { - return PackageParser.generatePermissionInfo(p.perm, flags); + if (p != null) { + return generatePermissionInfo(p, flags); } return null; } @@ -969,23 +1423,23 @@ class PackageManagerService extends IPackageManager.Stub { ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10); for (BasePermission p : mSettings.mPermissions.values()) { if (group == null) { - if (p.perm.info.group == null) { - out.add(PackageParser.generatePermissionInfo(p.perm, flags)); + if (p.perm == null || p.perm.info.group == null) { + out.add(generatePermissionInfo(p, flags)); } } else { - if (group.equals(p.perm.info.group)) { + if (p.perm != null && group.equals(p.perm.info.group)) { out.add(PackageParser.generatePermissionInfo(p.perm, flags)); } } } - + if (out.size() > 0) { return out; } return mPermissionGroups.containsKey(group) ? out : null; } } - + public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) { synchronized (mPackages) { return PackageParser.generatePermissionGroupInfo( @@ -1004,7 +1458,7 @@ class PackageManagerService extends IPackageManager.Stub { return out; } } - + private ApplicationInfo generateApplicationInfoFromSettingsLP(String packageName, int flags) { PackageSetting ps = mSettings.mPackages.get(packageName); if(ps != null) { @@ -1019,19 +1473,23 @@ class PackageManagerService extends IPackageManager.Stub { } return null; } - + private PackageInfo generatePackageInfoFromSettingsLP(String packageName, int flags) { PackageSetting ps = mSettings.mPackages.get(packageName); if(ps != null) { if(ps.pkg == null) { ps.pkg = new PackageParser.Package(packageName); ps.pkg.applicationInfo.packageName = packageName; + ps.pkg.applicationInfo.flags = ps.pkgFlags; + ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString; + ps.pkg.applicationInfo.sourceDir = ps.codePathString; + ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath(); } return generatePackageInfo(ps.pkg, flags); } return null; } - + public ApplicationInfo getApplicationInfo(String packageName, int flags) { synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); @@ -1051,8 +1509,8 @@ class PackageManagerService extends IPackageManager.Stub { } return null; } - - + + public void freeStorageAndNotify(final long freeStorageSize, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); @@ -1064,14 +1522,14 @@ class PackageManagerService extends IPackageManager.Stub { if (mInstaller != null) { retCode = mInstaller.freeCache(freeStorageSize); if (retCode < 0) { - Log.w(TAG, "Couldn't clear application caches"); + Slog.w(TAG, "Couldn't clear application caches"); } } //end if mInstaller if (observer != null) { try { observer.onRemoveCompleted(null, (retCode >= 0)); } catch (RemoteException e) { - Log.w(TAG, "RemoveException when invoking call back"); + Slog.w(TAG, "RemoveException when invoking call back"); } } } @@ -1089,7 +1547,7 @@ class PackageManagerService extends IPackageManager.Stub { if (mInstaller != null) { retCode = mInstaller.freeCache(freeStorageSize); if (retCode < 0) { - Log.w(TAG, "Couldn't clear application caches"); + Slog.w(TAG, "Couldn't clear application caches"); } } if(pi != null) { @@ -1099,13 +1557,13 @@ class PackageManagerService extends IPackageManager.Stub { pi.sendIntent(null, code, null, null, null); } catch (SendIntentException e1) { - Log.i(TAG, "Failed to send pending intent"); + Slog.i(TAG, "Failed to send pending intent"); } } } }); } - + public ActivityInfo getActivityInfo(ComponentName component, int flags) { synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); @@ -1144,7 +1602,7 @@ class PackageManagerService extends IPackageManager.Stub { } return null; } - + public String[] getSystemSharedLibraryNames() { Set<String> libSet; synchronized (mPackages) { @@ -1182,7 +1640,7 @@ class PackageManagerService extends IPackageManager.Stub { return mAvailableFeatures.containsKey(name); } } - + public int checkPermission(String permName, String pkgName) { synchronized (mPackages) { PackageParser.Package p = mPackages.get(pkgName); @@ -1204,16 +1662,9 @@ class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { Object obj = mSettings.getUserIdLP(uid); if (obj != null) { - if (obj instanceof SharedUserSetting) { - SharedUserSetting sus = (SharedUserSetting)obj; - if (sus.grantedPermissions.contains(permName)) { - return PackageManager.PERMISSION_GRANTED; - } - } else if (obj instanceof PackageSetting) { - PackageSetting ps = (PackageSetting)obj; - if (ps.grantedPermissions.contains(permName)) { - return PackageManager.PERMISSION_GRANTED; - } + GrantedPermissions gp = (GrantedPermissions)obj; + if (gp.grantedPermissions.contains(permName)) { + return PackageManager.PERMISSION_GRANTED; } } else { HashSet<String> perms = mSystemPermissions.get(uid); @@ -1252,31 +1703,84 @@ class PackageManagerService extends IPackageManager.Stub { throw new SecurityException("No permission tree found for " + permName); } + static boolean compareStrings(CharSequence s1, CharSequence s2) { + if (s1 == null) { + return s2 == null; + } + if (s2 == null) { + return false; + } + if (s1.getClass() != s2.getClass()) { + return false; + } + return s1.equals(s2); + } + + static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) { + if (pi1.icon != pi2.icon) return false; + if (pi1.protectionLevel != pi2.protectionLevel) return false; + if (!compareStrings(pi1.name, pi2.name)) return false; + if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false; + // We'll take care of setting this one. + if (!compareStrings(pi1.packageName, pi2.packageName)) return false; + // These are not currently stored in settings. + //if (!compareStrings(pi1.group, pi2.group)) return false; + //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false; + //if (pi1.labelRes != pi2.labelRes) return false; + //if (pi1.descriptionRes != pi2.descriptionRes) return false; + return true; + } + + boolean addPermissionLocked(PermissionInfo info, boolean async) { + if (info.labelRes == 0 && info.nonLocalizedLabel == null) { + throw new SecurityException("Label must be specified in permission"); + } + BasePermission tree = checkPermissionTreeLP(info.name); + BasePermission bp = mSettings.mPermissions.get(info.name); + boolean added = bp == null; + boolean changed = true; + if (added) { + bp = new BasePermission(info.name, tree.sourcePackage, + BasePermission.TYPE_DYNAMIC); + } else if (bp.type != BasePermission.TYPE_DYNAMIC) { + throw new SecurityException( + "Not allowed to modify non-dynamic permission " + + info.name); + } else { + if (bp.protectionLevel == info.protectionLevel + && bp.perm.owner.equals(tree.perm.owner) + && bp.uid == tree.uid + && comparePermissionInfos(bp.perm.info, info)) { + changed = false; + } + } + bp.protectionLevel = info.protectionLevel; + bp.perm = new PackageParser.Permission(tree.perm.owner, + new PermissionInfo(info)); + bp.perm.info.packageName = tree.perm.info.packageName; + bp.uid = tree.uid; + if (added) { + mSettings.mPermissions.put(info.name, bp); + } + if (changed) { + if (!async) { + mSettings.writeLP(); + } else { + scheduleWriteSettingsLocked(); + } + } + return added; + } + public boolean addPermission(PermissionInfo info) { synchronized (mPackages) { - if (info.labelRes == 0 && info.nonLocalizedLabel == null) { - throw new SecurityException("Label must be specified in permission"); - } - BasePermission tree = checkPermissionTreeLP(info.name); - BasePermission bp = mSettings.mPermissions.get(info.name); - boolean added = bp == null; - if (added) { - bp = new BasePermission(info.name, tree.sourcePackage, - BasePermission.TYPE_DYNAMIC); - } else if (bp.type != BasePermission.TYPE_DYNAMIC) { - throw new SecurityException( - "Not allowed to modify non-dynamic permission " - + info.name); - } - bp.perm = new PackageParser.Permission(tree.perm.owner, - new PermissionInfo(info)); - bp.perm.info.packageName = tree.perm.info.packageName; - bp.uid = tree.uid; - if (added) { - mSettings.mPermissions.put(info.name, bp); - } - mSettings.writeLP(); - return added; + return addPermissionLocked(info, false); + } + } + + public boolean addPermissionAsync(PermissionInfo info) { + synchronized (mPackages) { + return addPermissionLocked(info, true); } } @@ -1301,7 +1805,7 @@ class PackageManagerService extends IPackageManager.Stub { return mProtectedBroadcasts.contains(actionName); } } - + public int checkSignatures(String pkg1, String pkg2) { synchronized (mPackages) { PackageParser.Package p1 = mPackages.get(pkg1); @@ -1355,21 +1859,19 @@ class PackageManagerService extends IPackageManager.Stub { if (s2 == null) { return PackageManager.SIGNATURE_SECOND_NOT_SIGNED; } - final int N1 = s1.length; - final int N2 = s2.length; - for (int i=0; i<N1; i++) { - boolean match = false; - for (int j=0; j<N2; j++) { - if (s1[i].equals(s2[j])) { - match = true; - break; - } - } - if (!match) { - return PackageManager.SIGNATURE_NO_MATCH; - } + HashSet<Signature> set1 = new HashSet<Signature>(); + for (Signature sig : s1) { + set1.add(sig); + } + HashSet<Signature> set2 = new HashSet<Signature>(); + for (Signature sig : s2) { + set2.add(sig); } - return PackageManager.SIGNATURE_MATCH; + // Make sure s2 contains all signatures in s1. + if (set1.equals(set2)) { + return PackageManager.SIGNATURE_MATCH; + } + return PackageManager.SIGNATURE_NO_MATCH; } public String[] getPackagesForUid(int uid) { @@ -1406,7 +1908,7 @@ class PackageManagerService extends IPackageManager.Stub { } return null; } - + public int getUidForSharedUser(String sharedUserName) { if(sharedUserName == null) { return -1; @@ -1513,7 +2015,7 @@ class PackageManagerService extends IPackageManager.Stub { // was created, we need to clear it and re-ask the // user their preference. if (!pa.sameSet(query, priority)) { - Log.i(TAG, "Result set changed, dropping preferred activity for " + Slog.i(TAG, "Result set changed, dropping preferred activity for " + intent + " type " + resolvedType); mSettings.mPreferredActivities.removeFilter(pa); return null; @@ -1732,7 +2234,7 @@ class PackageManagerService extends IPackageManager.Stub { } return list; } - + synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { @@ -1790,7 +2292,7 @@ class PackageManagerService extends IPackageManager.Stub { return null; } } - + public List<PackageInfo> getInstalledPackages(int flags) { ArrayList<PackageInfo> finalList = new ArrayList<PackageInfo>(); @@ -1970,24 +2472,37 @@ class PackageManagerService extends IPackageManager.Stub { int i; for (i=0; i<files.length; i++) { File file = new File(dir, files[i]); - File resFile = file; - // Pick up the resource path from settings for fwd locked apps - if ((scanMode & SCAN_FORWARD_LOCKED) != 0) { - resFile = null; + if (!isPackageFilename(files[i])) { + // Ignore entries which are not apk's + continue; } - PackageParser.Package pkg = scanPackageLI(file, file, resFile, + PackageParser.Package pkg = scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode); + // Don't mess around with apps in system partition. + if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && + mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) { + // Delete the apk + Slog.w(TAG, "Cleaning up failed install of " + file); + file.delete(); + } } } + private static File getSettingsProblemFile() { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + File fname = new File(systemDir, "uiderrors.txt"); + return fname; + } + private static void reportSettingsProblem(int priority, String msg) { try { - File dataDir = Environment.getDataDirectory(); - File systemDir = new File(dataDir, "system"); - File fname = new File(systemDir, "uiderrors.txt"); + File fname = getSettingsProblemFile(); FileOutputStream out = new FileOutputStream(fname, true); PrintWriter pw = new PrintWriter(out); - pw.println(msg); + SimpleDateFormat formatter = new SimpleDateFormat(); + String dateString = formatter.format(new Date(System.currentTimeMillis())); + pw.println(dateString + ": " + msg); pw.close(); FileUtils.setPermissions( fname.toString(), @@ -1995,68 +2510,91 @@ class PackageManagerService extends IPackageManager.Stub { -1, -1); } catch (java.io.IOException e) { } - Log.println(priority, TAG, msg); + Slog.println(priority, TAG, msg); } private boolean collectCertificatesLI(PackageParser pp, PackageSetting ps, PackageParser.Package pkg, File srcFile, int parseFlags) { if (GET_CERTIFICATES) { - if (ps == null || !ps.codePath.equals(srcFile) - || ps.getTimeStamp() != srcFile.lastModified()) { - Log.i(TAG, srcFile.toString() + " changed; collecting certs"); - if (!pp.collectCertificates(pkg, parseFlags)) { - mLastScanError = pp.getParseError(); - return false; + if (ps != null + && ps.codePath.equals(srcFile) + && ps.getTimeStamp() == srcFile.lastModified()) { + if (ps.signatures.mSignatures != null + && ps.signatures.mSignatures.length != 0) { + // Optimization: reuse the existing cached certificates + // if the package appears to be unchanged. + pkg.mSignatures = ps.signatures.mSignatures; + return true; } + + Slog.w(TAG, "PackageSetting for " + ps.name + " is missing signatures. Collecting certs again to recover them."); + } else { + Log.i(TAG, srcFile.toString() + " changed; collecting certs"); + } + + if (!pp.collectCertificates(pkg, parseFlags)) { + mLastScanError = pp.getParseError(); + return false; } } return true; } - + /* * Scan a package and return the newly parsed package. * Returns null in case of errors and the error code is stored in mLastScanError */ private PackageParser.Package scanPackageLI(File scanFile, - File destCodeFile, File destResourceFile, int parseFlags, - int scanMode) { + int parseFlags, int scanMode) { mLastScanError = PackageManager.INSTALL_SUCCEEDED; + String scanPath = scanFile.getPath(); parseFlags |= mDefParseFlags; - PackageParser pp = new PackageParser(scanFile.getPath()); + PackageParser pp = new PackageParser(scanPath); pp.setSeparateProcesses(mSeparateProcesses); final PackageParser.Package pkg = pp.parsePackage(scanFile, - destCodeFile.getAbsolutePath(), mMetrics, parseFlags); + scanPath, mMetrics, parseFlags); if (pkg == null) { mLastScanError = pp.getParseError(); return null; } - PackageSetting ps; + PackageSetting ps = null; PackageSetting updatedPkg; synchronized (mPackages) { - ps = mSettings.peekPackageLP(pkg.packageName); - updatedPkg = mSettings.mDisabledSysPackages.get(pkg.packageName); - } - // Verify certificates first - if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) { - Log.i(TAG, "Failed verifying certificates for package:" + pkg.packageName); - return null; - } - if (updatedPkg != null) { - // An updated system app will not have the PARSE_IS_SYSTEM flag set initially - parseFlags |= PackageParser.PARSE_IS_SYSTEM; - } - if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { - // Check for updated system applications here - if ((ps != null) && (!ps.codePath.equals(scanFile))) { + // Look to see if we already know about this package. + String oldName = mSettings.mRenamedPackages.get(pkg.packageName); + if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) { + // This package has been renamed to its original name. Let's + // use that. + ps = mSettings.peekPackageLP(oldName); + } + // If there was no original package, see one for the real package name. + if (ps == null) { + ps = mSettings.peekPackageLP(pkg.packageName); + } + // Check to see if this package could be hiding/updating a system + // package. Must look for it either under the original or real + // package name depending on our state. + updatedPkg = mSettings.mDisabledSysPackages.get( + ps != null ? ps.name : pkg.packageName); + } + // First check if this is a system package that may involve an update + if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { + if (!ps.codePath.equals(scanFile)) { + // The path has changed from what was last scanned... check the + // version of the new path against what we have stored to determine + // what to do. if (pkg.mVersionCode < ps.versionCode) { // The system package has been updated and the code path does not match - // Ignore entry. Just return - Log.w(TAG, "Package:" + pkg.packageName + - " has been updated. Ignoring the one from path:"+scanFile); + // Ignore entry. Skip it. + Log.i(TAG, "Package " + ps.name + " at " + scanFile + + "ignored: updated version " + ps.versionCode + + " better than this " + pkg.mVersionCode); mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; return null; } else { - // Delete the older apk pointed to by ps + // The current app on the system partion is better than + // what we have updated to on the data partition; switch + // back to the system partition version. // At this point, its safely assumed that package installation for // apps in system partition will go through. If not there won't be a working // version of the app @@ -2064,23 +2602,57 @@ class PackageManagerService extends IPackageManager.Stub { // Just remove the loaded entries from package lists. mPackages.remove(ps.name); } - deletePackageResourcesLI(ps.name, ps.codePathString, ps.resourcePathString); + Slog.w(TAG, "Package " + ps.name + " at " + scanFile + + "reverting from " + ps.codePathString + + ": new version " + pkg.mVersionCode + + " better than installed " + ps.versionCode); + InstallArgs args = new FileInstallArgs(ps.codePathString, ps.resourcePathString); + args.cleanUpResourcesLI(); + removeNativeBinariesLI(pkg); mSettings.enableSystemPackageLP(ps.name); } } } + if (updatedPkg != null) { + // An updated system app will not have the PARSE_IS_SYSTEM flag set initially + parseFlags |= PackageParser.PARSE_IS_SYSTEM; + } + // Verify certificates against what was last scanned + if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) { + Slog.w(TAG, "Failed verifying certificates for package:" + pkg.packageName); + return null; + } // The apk is forward locked (not public) if its code and resources // are kept in different files. + // TODO grab this value from PackageSettings if (ps != null && !ps.codePath.equals(ps.resourcePath)) { - scanMode |= SCAN_FORWARD_LOCKED; + parseFlags |= PackageParser.PARSE_FORWARD_LOCK; } - File resFile = destResourceFile; - if (ps != null && ((scanMode & SCAN_FORWARD_LOCKED) != 0)) { - resFile = getFwdLockedResource(ps.name); + + String codePath = null; + String resPath = null; + if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0) { + if (ps != null && ps.resourcePathString != null) { + resPath = ps.resourcePathString; + } else { + // Should not happen at all. Just log an error. + Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName); + } + } else { + resPath = pkg.mScanPath; } + codePath = pkg.mScanPath; + // Set application objects path explicitly. + setApplicationInfoPaths(pkg, codePath, resPath); // Note that we invoke the following method only if we are about to unpack an application - return scanPackageLI(scanFile, destCodeFile, resFile, - pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE); + return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE); + } + + private static void setApplicationInfoPaths(PackageParser.Package pkg, + String destCodePath, String destResPath) { + pkg.mPath = pkg.mScanPath = destCodePath; + pkg.applicationInfo.sourceDir = destCodePath; + pkg.applicationInfo.publicSourceDir = destResPath; } private static String fixProcessName(String defProcessName, @@ -2091,38 +2663,37 @@ class PackageManagerService extends IPackageManager.Stub { return processName; } - private boolean verifySignaturesLP(PackageSetting pkgSetting, - PackageParser.Package pkg, int parseFlags, boolean updateSignature) { - if (pkg.mSignatures != null) { - if (!pkgSetting.signatures.updateSignatures(pkg.mSignatures, - updateSignature)) { - Log.e(TAG, "Package " + pkg.packageName - + " signatures do not match the previously installed version; ignoring!"); - mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; - return false; - } - - if (pkgSetting.sharedUser != null) { - if (!pkgSetting.sharedUser.signatures.mergeSignatures( - pkg.mSignatures, updateSignature)) { - Log.e(TAG, "Package " + pkg.packageName - + " has no signatures that match those in shared user " - + pkgSetting.sharedUser.name + "; ignoring!"); - mLastScanError = PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; + private boolean verifySignaturesLP(PackageSetting pkgSetting, + PackageParser.Package pkg) { + if (pkgSetting.signatures.mSignatures != null) { + // Already existing package. Make sure signatures match + if (checkSignaturesLP(pkgSetting.signatures.mSignatures, pkg.mSignatures) != + PackageManager.SIGNATURE_MATCH) { + Slog.e(TAG, "Package " + pkg.packageName + + " signatures do not match the previously installed version; ignoring!"); + mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; return false; } + } + // Check for shared user signatures + if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) { + if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures, + pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { + Slog.e(TAG, "Package " + pkg.packageName + + " has no signatures that match those in shared user " + + pkgSetting.sharedUser.name + "; ignoring!"); + mLastScanError = PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; + return false; } - } else { - pkg.mSignatures = pkgSetting.signatures.mSignatures; } return true; } - + public boolean performDexOpt(String packageName) { if (!mNoDexOpt) { return false; } - + PackageParser.Package p; synchronized (mPackages) { p = mPackages.get(packageName); @@ -2134,11 +2705,11 @@ class PackageManagerService extends IPackageManager.Stub { return performDexOptLI(p, false) == DEX_OPT_PERFORMED; } } - + static final int DEX_OPT_SKIPPED = 0; static final int DEX_OPT_PERFORMED = 1; static final int DEX_OPT_FAILED = -1; - + private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) { boolean performed = false; if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0 && mInstaller != null) { @@ -2146,16 +2717,22 @@ class PackageManagerService extends IPackageManager.Stub { int ret = 0; try { if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { - ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, - !pkg.mForwardLocked); + ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, + !isForwardLocked(pkg)); pkg.mDidDexOpt = true; performed = true; } } catch (FileNotFoundException e) { - Log.w(TAG, "Apk not found for dexopt: " + path); + Slog.w(TAG, "Apk not found for dexopt: " + path); ret = -1; } catch (IOException e) { - Log.w(TAG, "Exception reading apk: " + path, e); + Slog.w(TAG, "IOException reading apk: " + path, e); + ret = -1; + } catch (dalvik.system.StaleDexCacheError e) { + Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e); + ret = -1; + } catch (Exception e) { + Slog.w(TAG, "Exception when doing dexopt : ", e); ret = -1; } if (ret < 0) { @@ -2163,36 +2740,60 @@ class PackageManagerService extends IPackageManager.Stub { return DEX_OPT_FAILED; } } - + return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED; } - private PackageParser.Package scanPackageLI( - File scanFile, File destCodeFile, File destResourceFile, - PackageParser.Package pkg, int parseFlags, int scanMode) { + private boolean verifyPackageUpdate(PackageSetting oldPkg, PackageParser.Package newPkg) { + if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { + Slog.w(TAG, "Unable to update from " + oldPkg.name + + " to " + newPkg.packageName + + ": old package not in system partition"); + return false; + } else if (mPackages.get(oldPkg.name) != null) { + Slog.w(TAG, "Unable to update from " + oldPkg.name + + " to " + newPkg.packageName + + ": old package still exists"); + return false; + } + return true; + } + private File getDataPathForPackage(PackageParser.Package pkg) { + return new File(mAppDataDir, pkg.packageName); + } + + private PackageParser.Package scanPackageLI(PackageParser.Package pkg, + int parseFlags, int scanMode) { + File scanFile = new File(pkg.mScanPath); + if (scanFile == null || pkg.applicationInfo.sourceDir == null || + pkg.applicationInfo.publicSourceDir == null) { + // Bail out. The resource and code paths haven't been set. + Slog.w(TAG, " Code and resource paths haven't been set correctly"); + mLastScanError = PackageManager.INSTALL_FAILED_INVALID_APK; + return null; + } mScanningPath = scanFile; if (pkg == null) { mLastScanError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; return null; } - final String pkgName = pkg.applicationInfo.packageName; if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; } - if (pkgName.equals("android")) { + if (pkg.packageName.equals("android")) { synchronized (mPackages) { if (mAndroidApplication != null) { - Log.w(TAG, "*************************************************"); - Log.w(TAG, "Core android package being redefined. Skipping."); - Log.w(TAG, " file=" + mScanningPath); - Log.w(TAG, "*************************************************"); + Slog.w(TAG, "*************************************************"); + Slog.w(TAG, "Core android package being redefined. Skipping."); + Slog.w(TAG, " file=" + mScanningPath); + Slog.w(TAG, "*************************************************"); mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; return null; } - + // Set up information for our fall-back user intent resolution // activity. mPlatformPackage = pkg; @@ -2217,20 +2818,30 @@ class PackageManagerService extends IPackageManager.Stub { } if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) Log.d( - TAG, "Scanning package " + pkgName); - if (mPackages.containsKey(pkgName) || mSharedLibraries.containsKey(pkgName)) { - Log.w(TAG, "*************************************************"); - Log.w(TAG, "Application package " + pkgName + TAG, "Scanning package " + pkg.packageName); + if (mPackages.containsKey(pkg.packageName) + || mSharedLibraries.containsKey(pkg.packageName)) { + Slog.w(TAG, "*************************************************"); + Slog.w(TAG, "Application package " + pkg.packageName + " already installed. Skipping duplicate."); - Log.w(TAG, "*************************************************"); + Slog.w(TAG, "*************************************************"); mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; return null; } + // Initialize package source and resource directories + File destCodeFile = new File(pkg.applicationInfo.sourceDir); + File destResourceFile = new File(pkg.applicationInfo.publicSourceDir); + SharedUserSetting suid = null; PackageSetting pkgSetting = null; - - boolean removeExisting = false; + + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) { + // Only system apps can use these features. + pkg.mOriginalPackages = null; + pkg.mRealPackage = null; + pkg.mAdoptPermissions = null; + } synchronized (mPackages) { // Check all shared libraries and map to their actual file path. @@ -2244,7 +2855,7 @@ class PackageManagerService extends IPackageManager.Stub { for (int i=0; i<N; i++) { String file = mSharedLibraries.get(pkg.usesLibraries.get(i)); if (file == null) { - Log.e(TAG, "Package " + pkg.packageName + Slog.e(TAG, "Package " + pkg.packageName + " requires unavailable shared library " + pkg.usesLibraries.get(i) + "; failing!"); mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; @@ -2257,7 +2868,7 @@ class PackageManagerService extends IPackageManager.Stub { for (int i=0; i<N; i++) { String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i)); if (file == null) { - Log.w(TAG, "Package " + pkg.packageName + Slog.w(TAG, "Package " + pkg.packageName + " desires unavailable shared library " + pkg.usesOptionalLibraries.get(i) + "; ignoring!"); } else { @@ -2270,7 +2881,7 @@ class PackageManagerService extends IPackageManager.Stub { System.arraycopy(mTmpSharedLibraries, 0, pkg.usesLibraryFiles, 0, num); } - + if (pkg.reqFeatures != null) { N = pkg.reqFeatures.size(); for (int i=0; i<N; i++) { @@ -2279,10 +2890,10 @@ class PackageManagerService extends IPackageManager.Stub { // Don't care. continue; } - + if (fi.name != null) { if (mAvailableFeatures.get(fi.name) == null) { - Log.e(TAG, "Package " + pkg.packageName + Slog.e(TAG, "Package " + pkg.packageName + " requires unavailable feature " + fi.name + "; failing!"); mLastScanError = PackageManager.INSTALL_FAILED_MISSING_FEATURE; @@ -2292,12 +2903,12 @@ class PackageManagerService extends IPackageManager.Stub { } } } - + if (pkg.mSharedUserId != null) { suid = mSettings.getSharedUserLP(pkg.mSharedUserId, pkg.applicationInfo.flags, true); if (suid == null) { - Log.w(TAG, "Creating application package " + pkgName + Slog.w(TAG, "Creating application package " + pkg.packageName + " for shared user failed"); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; @@ -2308,26 +2919,113 @@ class PackageManagerService extends IPackageManager.Stub { } } + if (false) { + if (pkg.mOriginalPackages != null) { + Log.w(TAG, "WAITING FOR DEBUGGER"); + Debug.waitForDebugger(); + Log.i(TAG, "Package " + pkg.packageName + " from original packages" + + pkg.mOriginalPackages); + } + } + + // Check if we are renaming from an original package name. + PackageSetting origPackage = null; + String realName = null; + if (pkg.mOriginalPackages != null) { + // This package may need to be renamed to a previously + // installed name. Let's check on that... + String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage); + if (pkg.mOriginalPackages.contains(renamed)) { + // This package had originally been installed as the + // original name, and we have already taken care of + // transitioning to the new one. Just update the new + // one to continue using the old name. + realName = pkg.mRealPackage; + if (!pkg.packageName.equals(renamed)) { + // Callers into this function may have already taken + // care of renaming the package; only do it here if + // it is not already done. + pkg.setPackageName(renamed); + } + + } else { + for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) { + if ((origPackage=mSettings.peekPackageLP( + pkg.mOriginalPackages.get(i))) != null) { + // We do have the package already installed under its + // original name... should we use it? + if (!verifyPackageUpdate(origPackage, pkg)) { + // New package is not compatible with original. + origPackage = null; + continue; + } else if (origPackage.sharedUser != null) { + // Make sure uid is compatible between packages. + if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) { + Slog.w(TAG, "Unable to migrate data from " + origPackage.name + + " to " + pkg.packageName + ": old uid " + + origPackage.sharedUser.name + + " differs from " + pkg.mSharedUserId); + origPackage = null; + continue; + } + } else { + if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package " + + pkg.packageName + " to old name " + origPackage.name); + } + break; + } + } + } + } + + if (mTransferedPackages.contains(pkg.packageName)) { + Slog.w(TAG, "Package " + pkg.packageName + + " was transferred to another, but its .apk remains"); + } + // Just create the setting, don't add it yet. For already existing packages // the PkgSetting exists already and doesn't have to be created. - pkgSetting = mSettings.getPackageLP(pkg, suid, destCodeFile, + pkgSetting = mSettings.getPackageLP(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.flags, true, false); if (pkgSetting == null) { - Log.w(TAG, "Creating application package " + pkgName + " failed"); + Slog.w(TAG, "Creating application package " + pkg.packageName + " failed"); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } - if(mSettings.mDisabledSysPackages.get(pkg.packageName) != null) { + + if (pkgSetting.origPackage != null) { + // If we are first transitioning from an original package, + // fix up the new package's name now. We need to do this after + // looking up the package under its new name, so getPackageLP + // can take care of fiddling things correctly. + pkg.setPackageName(origPackage.name); + + // File a report about this. + String msg = "New package " + pkgSetting.realName + + " renamed to replace old package " + pkgSetting.name; + reportSettingsProblem(Log.WARN, msg); + + // Make a note of it. + mTransferedPackages.add(origPackage.name); + + // No longer need to retain this. + pkgSetting.origPackage = null; + } + + if (realName != null) { + // Make a note of it. + mTransferedPackages.add(pkg.packageName); + } + + if (mSettings.mDisabledSysPackages.get(pkg.packageName) != null) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } - + pkg.applicationInfo.uid = pkgSetting.userId; pkg.mExtras = pkgSetting; - - if (!verifySignaturesLP(pkgSetting, pkg, parseFlags, - (scanMode&SCAN_UPDATE_SIGNATURE) != 0)) { - if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) == 0) { - mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; + + if (!verifySignaturesLP(pkgSetting, pkg)) { + if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { return null; } // The signature has changed, but this package is in the system @@ -2339,15 +3037,19 @@ class PackageManagerService extends IPackageManager.Stub { // associated with an overall shared user, which doesn't seem all // that unreasonable. if (pkgSetting.sharedUser != null) { - if (!pkgSetting.sharedUser.signatures.mergeSignatures( - pkg.mSignatures, false)) { + if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures, + pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { + Log.w(TAG, "Signature mismatch for shared user : " + pkgSetting.sharedUser); mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; return null; } } - removeExisting = true; + // File a report about this. + String msg = "System package " + pkg.packageName + + " signature changed; retaining data."; + reportSettingsProblem(Log.WARN, msg); } - + // Verify that this new package doesn't have any content providers // that conflict with existing packages. Only do this if the // package isn't already installed, since we don't want to break @@ -2357,37 +3059,41 @@ class PackageManagerService extends IPackageManager.Stub { int i; for (i=0; i<N; i++) { PackageParser.Provider p = pkg.providers.get(i); - String names[] = p.info.authority.split(";"); - for (int j = 0; j < names.length; j++) { - if (mProviders.containsKey(names[j])) { - PackageParser.Provider other = mProviders.get(names[j]); - Log.w(TAG, "Can't install because provider name " + names[j] + - " (in package " + pkg.applicationInfo.packageName + - ") is already used by " - + ((other != null && other.component != null) - ? other.component.getPackageName() : "?")); - mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER; - return null; + if (p.info.authority != null) { + String names[] = p.info.authority.split(";"); + for (int j = 0; j < names.length; j++) { + if (mProviders.containsKey(names[j])) { + PackageParser.Provider other = mProviders.get(names[j]); + Slog.w(TAG, "Can't install because provider name " + names[j] + + " (in package " + pkg.applicationInfo.packageName + + ") is already used by " + + ((other != null && other.getComponentName() != null) + ? other.getComponentName().getPackageName() : "?")); + mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER; + return null; + } } } } } } - if (removeExisting) { - if (mInstaller != null) { - int ret = mInstaller.remove(pkgName); - if (ret != 0) { - String msg = "System package " + pkg.packageName - + " could not have data directory erased after signature change."; - reportSettingsProblem(Log.WARN, msg); - mLastScanError = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; - return null; + final String pkgName = pkg.packageName; + + if (pkg.mAdoptPermissions != null) { + // This package wants to adopt ownership of permissions from + // another package. + for (int i=pkg.mAdoptPermissions.size()-1; i>=0; i--) { + String origName = pkg.mAdoptPermissions.get(i); + PackageSetting orig = mSettings.peekPackageLP(origName); + if (orig != null) { + if (verifyPackageUpdate(orig, pkg)) { + Slog.i(TAG, "Adopting permissions from " + + origName + " to " + pkg.packageName); + mSettings.transferPermissions(origName, pkg.packageName); + } } } - Log.w(TAG, "System package " + pkg.packageName - + " signature changed: existing data removed."); - mLastScanError = PackageManager.INSTALL_SUCCEEDED; } long scanFileTime = scanFile.lastModified(); @@ -2397,7 +3103,6 @@ class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.packageName, pkg.applicationInfo.processName, pkg.applicationInfo.uid); - pkg.applicationInfo.publicSourceDir = destResourceFile.toString(); File dataPath; if (mPlatformPackage == pkg) { @@ -2406,7 +3111,10 @@ class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.dataDir = dataPath.getPath(); } else { // This is a normal package, need to make its data directory. - dataPath = new File(mAppDataDir, pkgName); + dataPath = getDataPathForPackage(pkg); + + boolean uidError = false; + if (dataPath.exists()) { mOutPermissions[1] = 0; FileUtils.getPermissions(dataPath.getPath(), mOutPermissions); @@ -2420,7 +3128,7 @@ class PackageManagerService extends IPackageManager.Stub { // current data so the application will still work. if (mInstaller != null) { int ret = mInstaller.remove(pkgName); - if(ret >= 0) { + if (ret >= 0) { // Old data gone! String msg = "System package " + pkg.packageName + " has changed from uid: " @@ -2428,7 +3136,7 @@ class PackageManagerService extends IPackageManager.Stub { + pkg.applicationInfo.uid + "; old data erased"; reportSettingsProblem(Log.WARN, msg); recovered = true; - + // And now re-install the app. ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); @@ -2441,7 +3149,7 @@ class PackageManagerService extends IPackageManager.Stub { return null; } } - } + } if (!recovered) { mHasSystemUidErrors = true; } @@ -2455,12 +3163,12 @@ class PackageManagerService extends IPackageManager.Stub { + mOutPermissions[1] + " on disk, " + pkg.applicationInfo.uid + " in settings"; synchronized (mPackages) { - if (!mReportedUidError) { - mReportedUidError = true; - msg = msg + "; read messages:\n" - + mSettings.getReadMessagesLP(); + mSettings.mReadMessages.append(msg); + mSettings.mReadMessages.append('\n'); + uidError = true; + if (!pkgSetting.uidError) { + reportSettingsProblem(Log.ERROR, msg); } - reportSettingsProblem(Log.ERROR, msg); } } } @@ -2489,10 +3197,12 @@ class PackageManagerService extends IPackageManager.Stub { if (dataPath.exists()) { pkg.applicationInfo.dataDir = dataPath.getPath(); } else { - Log.w(TAG, "Unable to create data directory: " + dataPath); + Slog.w(TAG, "Unable to create data directory: " + dataPath); pkg.applicationInfo.dataDir = null; } } + + pkgSetting.uidError = uidError; } // Perform shared library installation and dex validation and @@ -2500,17 +3210,26 @@ class PackageManagerService extends IPackageManager.Stub { if (mInstaller != null) { String path = scanFile.getPath(); if (scanFileNewer) { - Log.i(TAG, path + " changed; unpacking"); - int err = cachePackageSharedLibsLI(pkg, dataPath, scanFile); - if (err != PackageManager.INSTALL_SUCCEEDED) { - mLastScanError = err; - return null; + // Note: We don't want to unpack the native binaries for + // system applications, unless they have been updated + // (the binaries are already under /system/lib). + // + // In other words, we're going to unpack the binaries + // only for non-system apps and system app upgrades. + // + int flags = pkg.applicationInfo.flags; + if ((flags & ApplicationInfo.FLAG_SYSTEM) == 0 || + (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + Log.i(TAG, path + " changed; unpacking"); + int err = cachePackageSharedLibsLI(pkg, scanFile); + if (err != PackageManager.INSTALL_SUCCEEDED) { + mLastScanError = err; + return null; + } } } - - pkg.mForwardLocked = (scanMode&SCAN_FORWARD_LOCKED) != 0; pkg.mScanPath = path; - + if ((scanMode&SCAN_NO_DEX) == 0) { if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; @@ -2518,34 +3237,32 @@ class PackageManagerService extends IPackageManager.Stub { } } } - + if (mFactoryTest && pkg.requestedPermissions.contains( android.Manifest.permission.FACTORY_TEST)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST; } - // We don't expect installation to fail beyond this point, - if ((scanMode&SCAN_MONITOR) != 0) { - pkg.mPath = destCodeFile.getAbsolutePath(); - mAppDirs.put(pkg.mPath, pkg); - } - // Request the ActivityManager to kill the process(only for existing packages) // so that we do not end up in a confused state while the user is still using the older // version of the application while the new one gets installed. - IActivityManager am = ActivityManagerNative.getDefault(); - if ((am != null) && ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING ) != 0)) { - try { - am.killApplicationWithUid(pkg.applicationInfo.packageName, + if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { + killApplication(pkg.applicationInfo.packageName, pkg.applicationInfo.uid); - } catch (RemoteException e) { - } } + synchronized (mPackages) { + // We don't expect installation to fail beyond this point, + if ((scanMode&SCAN_MONITOR) != 0) { + mAppDirs.put(pkg.mPath, pkg); + } // Add the new setting to mSettings - mSettings.insertPackageSettingLP(pkgSetting, pkg, destCodeFile, destResourceFile); + mSettings.insertPackageSettingLP(pkgSetting, pkg); // Add the new setting to mPackages mPackages.put(pkg.applicationInfo.packageName, pkg); + // Make sure we don't accidentally delete its data. + mSettings.mPackagesToBeCleaned.remove(pkgName); + int N = pkg.providers.size(); StringBuilder r = null; int i; @@ -2556,38 +3273,40 @@ class PackageManagerService extends IPackageManager.Stub { mProvidersByComponent.put(new ComponentName(p.info.packageName, p.info.name), p); p.syncable = p.info.isSyncable; - String names[] = p.info.authority.split(";"); - p.info.authority = null; - for (int j = 0; j < names.length; j++) { - if (j == 1 && p.syncable) { - // We only want the first authority for a provider to possibly be - // syncable, so if we already added this provider using a different - // authority clear the syncable flag. We copy the provider before - // changing it because the mProviders object contains a reference - // to a provider that we don't want to change. - // Only do this for the second authority since the resulting provider - // object can be the same for all future authorities for this provider. - p = new PackageParser.Provider(p); - p.syncable = false; - } - if (!mProviders.containsKey(names[j])) { - mProviders.put(names[j], p); - if (p.info.authority == null) { - p.info.authority = names[j]; + if (p.info.authority != null) { + String names[] = p.info.authority.split(";"); + p.info.authority = null; + for (int j = 0; j < names.length; j++) { + if (j == 1 && p.syncable) { + // We only want the first authority for a provider to possibly be + // syncable, so if we already added this provider using a different + // authority clear the syncable flag. We copy the provider before + // changing it because the mProviders object contains a reference + // to a provider that we don't want to change. + // Only do this for the second authority since the resulting provider + // object can be the same for all future authorities for this provider. + p = new PackageParser.Provider(p); + p.syncable = false; + } + if (!mProviders.containsKey(names[j])) { + mProviders.put(names[j], p); + if (p.info.authority == null) { + p.info.authority = names[j]; + } else { + p.info.authority = p.info.authority + ";" + names[j]; + } + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) + Log.d(TAG, "Registered content provider: " + names[j] + + ", className = " + p.info.name + + ", isSyncable = " + p.info.isSyncable); } else { - p.info.authority = p.info.authority + ";" + names[j]; + PackageParser.Provider other = mProviders.get(names[j]); + Slog.w(TAG, "Skipping provider name " + names[j] + + " (in package " + pkg.applicationInfo.packageName + + "): name already used by " + + ((other != null && other.getComponentName() != null) + ? other.getComponentName().getPackageName() : "?")); } - if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) - Log.d(TAG, "Registered content provider: " + names[j] + - ", className = " + p.info.name + - ", isSyncable = " + p.info.isSyncable); - } else { - PackageParser.Provider other = mProviders.get(names[j]); - Log.w(TAG, "Skipping provider name " + names[j] + - " (in package " + pkg.applicationInfo.packageName + - "): name already used by " - + ((other != null && other.component != null) - ? other.component.getPackageName() : "?")); } } if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { @@ -2602,7 +3321,7 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Providers: " + r); } - + N = pkg.services.size(); r = null; for (i=0; i<N; i++) { @@ -2622,7 +3341,7 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Services: " + r); } - + N = pkg.receivers.size(); r = null; for (i=0; i<N; i++) { @@ -2642,7 +3361,7 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Receivers: " + r); } - + N = pkg.activities.size(); r = null; for (i=0; i<N; i++) { @@ -2662,7 +3381,7 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Activities: " + r); } - + N = pkg.permissionGroups.size(); r = null; for (i=0; i<N; i++) { @@ -2679,7 +3398,7 @@ class PackageManagerService extends IPackageManager.Stub { r.append(pg.info.name); } } else { - Log.w(TAG, "Permission group " + pg.info.name + " from package " + Slog.w(TAG, "Permission group " + pg.info.name + " from package " + pg.info.packageName + " ignored: original from " + cur.info.packageName); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { @@ -2696,7 +3415,7 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Permission Groups: " + r); } - + N = pkg.permissions.size(); r = null; for (i=0; i<N; i++) { @@ -2718,6 +3437,7 @@ class PackageManagerService extends IPackageManager.Stub { BasePermission tree = findPermissionTreeLP(p.info.name); if (tree == null || tree.sourcePackage.equals(p.info.packageName)) { + bp.packageSetting = pkgSetting; bp.perm = p; bp.uid = pkg.applicationInfo.uid; if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { @@ -2729,13 +3449,13 @@ class PackageManagerService extends IPackageManager.Stub { r.append(p.info.name); } } else { - Log.w(TAG, "Permission " + p.info.name + " from package " + Slog.w(TAG, "Permission " + p.info.name + " from package " + p.info.packageName + " ignored: base tree " + tree.name + " is from package " + tree.sourcePackage); } } else { - Log.w(TAG, "Permission " + p.info.name + " from package " + Slog.w(TAG, "Permission " + p.info.name + " from package " + p.info.packageName + " ignored: original from " + bp.sourcePackage); } @@ -2748,8 +3468,11 @@ class PackageManagerService extends IPackageManager.Stub { r.append("DUP:"); r.append(p.info.name); } + if (bp.perm == p) { + bp.protectionLevel = p.info.protectionLevel; + } } else { - Log.w(TAG, "Permission " + p.info.name + " from package " + Slog.w(TAG, "Permission " + p.info.name + " from package " + p.info.packageName + " ignored: no group " + p.group); } @@ -2757,7 +3480,7 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Permissions: " + r); } - + N = pkg.instrumentation.size(); r = null; for (i=0; i<N; i++) { @@ -2766,7 +3489,7 @@ class PackageManagerService extends IPackageManager.Stub { a.info.sourceDir = pkg.applicationInfo.sourceDir; a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir; a.info.dataDir = pkg.applicationInfo.dataDir; - mInstrumentation.put(a.component, a); + mInstrumentation.put(a.getComponentName(), a); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); @@ -2779,20 +3502,33 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Instrumentation: " + r); } - + if (pkg.protectedBroadcasts != null) { N = pkg.protectedBroadcasts.size(); for (i=0; i<N; i++) { mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i)); } } - + pkgSetting.setTimeStamp(scanFileTime); } - + return pkg; } + private void killApplication(String pkgName, int uid) { + // Request the ActivityManager to kill the process(only for existing packages) + // so that we do not end up in a confused state while the user is still using the older + // version of the application while the new one gets installed. + IActivityManager am = ActivityManagerNative.getDefault(); + if (am != null) { + try { + am.killApplicationWithUid(pkgName, uid); + } catch (RemoteException e) { + } + } + } + // The following constants are returned by cachePackageSharedLibsForAbiLI // to indicate if native shared libraries were found in the package. // Values are: @@ -2805,6 +3541,13 @@ class PackageManagerService extends IPackageManager.Stub { private static final int PACKAGE_INSTALL_NATIVE_NO_LIBRARIES = 1; private static final int PACKAGE_INSTALL_NATIVE_ABI_MISMATCH = 2; + // Return the path of the directory that will contain the native binaries + // of a given installed package. This is relative to the data path. + // + private static File getNativeBinaryDirForPackage(PackageParser.Package pkg) { + return new File(pkg.applicationInfo.dataDir + "/lib"); + } + // Find all files of the form lib/<cpuAbi>/lib<name>.so in the .apk // and automatically copy them to /data/data/<appname>/lib if present. // @@ -2813,10 +3556,9 @@ class PackageManagerService extends IPackageManager.Stub { // room left on the data partition, or a ZipException if the package // file is malformed. // - private int cachePackageSharedLibsForAbiLI( PackageParser.Package pkg, - File dataPath, File scanFile, String cpuAbi) - throws IOException, ZipException { - File sharedLibraryDir = new File(dataPath.getPath() + "/lib"); + private int cachePackageSharedLibsForAbiLI(PackageParser.Package pkg, + File scanFile, String cpuAbi) throws IOException, ZipException { + File sharedLibraryDir = getNativeBinaryDirForPackage(pkg); final String apkLib = "lib/"; final int apkLibLen = apkLib.length(); final int cpuAbiLen = cpuAbi.length(); @@ -2858,7 +3600,7 @@ class PackageManagerService extends IPackageManager.Stub { // file name must start with libPrefix, i.e. "lib" int lastSlash = entryName.lastIndexOf('/'); - if (lastSlash < 0 || + if (lastSlash < 0 || !entryName.regionMatches(lastSlash+1, libPrefix, 0, libPrefixLen) ) { continue; } @@ -2893,7 +3635,7 @@ class PackageManagerService extends IPackageManager.Stub { if (mInstaller == null) { sharedLibraryDir.mkdir(); } - cacheSharedLibLI(pkg, zipFile, entry, sharedLibraryDir, + cacheNativeBinaryLI(pkg, zipFile, entry, sharedLibraryDir, sharedLibraryFile); } } @@ -2906,6 +3648,54 @@ class PackageManagerService extends IPackageManager.Stub { return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES; } + // Find the gdbserver executable program in a package at + // lib/<cpuAbi>/gdbserver and copy it to /data/data/<name>/lib/gdbserver + // + // Returns PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES on success, + // or PACKAGE_INSTALL_NATIVE_NO_LIBRARIES otherwise. + // + private int cachePackageGdbServerLI(PackageParser.Package pkg, + File scanFile, String cpuAbi) throws IOException, ZipException { + File installGdbServerDir = getNativeBinaryDirForPackage(pkg); + final String GDBSERVER = "gdbserver"; + final String apkGdbServerPath = "lib/" + cpuAbi + "/" + GDBSERVER; + + ZipFile zipFile = new ZipFile(scanFile); + Enumeration<ZipEntry> entries = + (Enumeration<ZipEntry>) zipFile.entries(); + + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + // skip directories + if (entry.isDirectory()) { + continue; + } + String entryName = entry.getName(); + + if (!entryName.equals(apkGdbServerPath)) { + continue; + } + + String installGdbServerPath = installGdbServerDir.getPath() + + "/" + GDBSERVER; + File installGdbServerFile = new File(installGdbServerPath); + if (! installGdbServerFile.exists() || + installGdbServerFile.length() != entry.getSize() || + installGdbServerFile.lastModified() != entry.getTime()) { + if (Config.LOGD) { + Log.d(TAG, "Caching gdbserver " + entry.getName()); + } + if (mInstaller == null) { + installGdbServerDir.mkdir(); + } + cacheNativeBinaryLI(pkg, zipFile, entry, installGdbServerDir, + installGdbServerFile); + } + return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES; + } + return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES; + } + // extract shared libraries stored in the APK as lib/<cpuAbi>/lib<name>.so // and copy them to /data/data/<appname>/lib. // @@ -2913,11 +3703,10 @@ class PackageManagerService extends IPackageManager.Stub { // (which corresponds to ro.product.cpu.abi), and also try an alternate // one if ro.product.cpu.abi2 is defined. // - private int cachePackageSharedLibsLI(PackageParser.Package pkg, - File dataPath, File scanFile) { - final String cpuAbi = Build.CPU_ABI; + private int cachePackageSharedLibsLI(PackageParser.Package pkg, File scanFile) { + String cpuAbi = Build.CPU_ABI; try { - int result = cachePackageSharedLibsForAbiLI(pkg, dataPath, scanFile, cpuAbi); + int result = cachePackageSharedLibsForAbiLI(pkg, scanFile, cpuAbi); // some architectures are capable of supporting several CPU ABIs // for example, 'armeabi-v7a' also supports 'armeabi' native code @@ -2926,64 +3715,110 @@ class PackageManagerService extends IPackageManager.Stub { // // only scan the package twice in case of ABI mismatch if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) { - String cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2",null); + final String cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2",null); if (cpuAbi2 != null) { - result = cachePackageSharedLibsForAbiLI(pkg, dataPath, scanFile, cpuAbi2); + result = cachePackageSharedLibsForAbiLI(pkg, scanFile, cpuAbi2); } if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) { - Log.w(TAG,"Native ABI mismatch from package file"); + Slog.w(TAG,"Native ABI mismatch from package file"); return PackageManager.INSTALL_FAILED_INVALID_APK; } + + if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) { + cpuAbi = cpuAbi2; + } + } + + // for debuggable packages, also extract gdbserver from lib/<abi> + // into /data/data/<appname>/lib too. + if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES && + (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + int result2 = cachePackageGdbServerLI(pkg, scanFile, cpuAbi); + if (result2 == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_NATIVE_DEBUGGABLE; + } } } catch (ZipException e) { - Log.w(TAG, "Failed to extract data from package file", e); + Slog.w(TAG, "Failed to extract data from package file", e); return PackageManager.INSTALL_FAILED_INVALID_APK; } catch (IOException e) { - Log.w(TAG, "Failed to cache package shared libs", e); + Slog.w(TAG, "Failed to cache package shared libs", e); return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } return PackageManager.INSTALL_SUCCEEDED; } - private void cacheSharedLibLI(PackageParser.Package pkg, + private void cacheNativeBinaryLI(PackageParser.Package pkg, ZipFile zipFile, ZipEntry entry, - File sharedLibraryDir, - File sharedLibraryFile) throws IOException { + File binaryDir, + File binaryFile) throws IOException { InputStream inputStream = zipFile.getInputStream(entry); try { - File tempFile = File.createTempFile("tmp", "tmp", sharedLibraryDir); + File tempFile = File.createTempFile("tmp", "tmp", binaryDir); String tempFilePath = tempFile.getPath(); - // XXX package manager can't change owner, so the lib files for + // XXX package manager can't change owner, so the executable files for // now need to be left as world readable and owned by the system. if (! FileUtils.copyToFile(inputStream, tempFile) || ! tempFile.setLastModified(entry.getTime()) || FileUtils.setPermissions(tempFilePath, FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP + |FileUtils.S_IXUSR|FileUtils.S_IXGRP|FileUtils.S_IXOTH |FileUtils.S_IROTH, -1, -1) != 0 || - ! tempFile.renameTo(sharedLibraryFile)) { + ! tempFile.renameTo(binaryFile)) { // Failed to properly write file. tempFile.delete(); - throw new IOException("Couldn't create cached shared lib " - + sharedLibraryFile + " in " + sharedLibraryDir); + throw new IOException("Couldn't create cached binary " + + binaryFile + " in " + binaryDir); } } finally { inputStream.close(); } } + // Remove the native binaries of a given package. This simply + // gets rid of the files in the 'lib' sub-directory. + private void removeNativeBinariesLI(PackageParser.Package pkg) { + File binaryDir = getNativeBinaryDirForPackage(pkg); + + if (DEBUG_NATIVE) { + Slog.w(TAG,"Deleting native binaries from: " + binaryDir.getPath()); + } + + // Just remove any file in the directory. Since the directory + // is owned by the 'system' UID, the application is not supposed + // to have written anything there. + // + if (binaryDir.exists()) { + File[] binaries = binaryDir.listFiles(); + if (binaries != null) { + for (int nn=0; nn < binaries.length; nn++) { + if (DEBUG_NATIVE) { + Slog.d(TAG," Deleting " + binaries[nn].getName()); + } + if (!binaries[nn].delete()) { + Slog.w(TAG,"Could not delete native binary: " + + binaries[nn].getPath()); + } + } + } + // Do not delete 'lib' directory itself, or this will prevent + // installation of future updates. + } + } + void removePackageLI(PackageParser.Package pkg, boolean chatty) { if (chatty && Config.LOGD) Log.d( TAG, "Removing package " + pkg.applicationInfo.packageName ); synchronized (mPackages) { clearPackagePreferredActivitiesLP(pkg.packageName); - + mPackages.remove(pkg.applicationInfo.packageName); if (pkg.mPath != null) { mAppDirs.remove(pkg.mPath); } - + PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps != null && ps.sharedUser != null) { // XXX don't do this until the data is removed. @@ -2994,7 +3829,7 @@ class PackageManagerService extends IPackageManager.Stub { } } } - + int N = pkg.providers.size(); StringBuilder r = null; int i; @@ -3003,7 +3838,7 @@ class PackageManagerService extends IPackageManager.Stub { mProvidersByComponent.remove(new ComponentName(p.info.packageName, p.info.name)); if (p.info.authority == null) { - + /* The is another ContentProvider with this authority when * this app was installed so this authority is null, * Ignore it as we don't have to unregister the provider. @@ -3032,7 +3867,7 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Providers: " + r); } - + N = pkg.services.size(); r = null; for (i=0; i<N; i++) { @@ -3050,7 +3885,7 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Services: " + r); } - + N = pkg.receivers.size(); r = null; for (i=0; i<N; i++) { @@ -3068,7 +3903,7 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Receivers: " + r); } - + N = pkg.activities.size(); r = null; for (i=0; i<N; i++) { @@ -3086,7 +3921,7 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Activities: " + r); } - + N = pkg.permissions.size(); r = null; for (i=0; i<N; i++) { @@ -3098,15 +3933,7 @@ class PackageManagerService extends IPackageManager.Stub { bp = mSettings.mPermissionTrees.get(p.info.name); } if (bp != null && bp.perm == p) { - if (bp.type != BasePermission.TYPE_BUILTIN) { - if (tree) { - mSettings.mPermissionTrees.remove(p.info.name); - } else { - mSettings.mPermissions.remove(p.info.name); - } - } else { - bp.perm = null; - } + bp.perm = null; if (chatty) { if (r == null) { r = new StringBuilder(256); @@ -3120,12 +3947,12 @@ class PackageManagerService extends IPackageManager.Stub { if (r != null) { if (Config.LOGD) Log.d(TAG, " Permissions: " + r); } - + N = pkg.instrumentation.size(); r = null; for (i=0; i<N; i++) { PackageParser.Instrumentation a = pkg.instrumentation.get(i); - mInstrumentation.remove(a.component); + mInstrumentation.remove(a.getComponentName()); if (chatty) { if (r == null) { r = new StringBuilder(256); @@ -3145,16 +3972,39 @@ class PackageManagerService extends IPackageManager.Stub { return name != null && name.endsWith(".apk"); } - private void updatePermissionsLP() { + private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) { + for (int i=pkgInfo.permissions.size()-1; i>=0; i--) { + if (pkgInfo.permissions.get(i).info.name.equals(perm)) { + return true; + } + } + return false; + } + + private void updatePermissionsLP(String changingPkg, + PackageParser.Package pkgInfo, boolean grantPermissions, + boolean replace, boolean replaceAll) { // Make sure there are no dangling permission trees. Iterator<BasePermission> it = mSettings.mPermissionTrees .values().iterator(); while (it.hasNext()) { BasePermission bp = it.next(); - if (bp.perm == null) { - Log.w(TAG, "Removing dangling permission tree: " + bp.name + if (bp.packageSetting == null) { + // We may not yet have parsed the package, so just see if + // we still know about its settings. + bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage); + } + if (bp.packageSetting == null) { + Slog.w(TAG, "Removing dangling permission tree: " + bp.name + " from package " + bp.sourcePackage); it.remove(); + } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) { + if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) { + Slog.i(TAG, "Removing old permission tree: " + bp.name + + " from package " + bp.sourcePackage); + grantPermissions = true; + it.remove(); + } } } @@ -3167,9 +4017,10 @@ class PackageManagerService extends IPackageManager.Stub { if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" + bp.name + " pkg=" + bp.sourcePackage + " info=" + bp.pendingInfo); - if (bp.perm == null && bp.pendingInfo != null) { + if (bp.packageSetting == null && bp.pendingInfo != null) { BasePermission tree = findPermissionTreeLP(bp.name); if (tree != null) { + bp.packageSetting = tree.packageSetting; bp.perm = new PackageParser.Permission(tree.perm.owner, new PermissionInfo(bp.pendingInfo)); bp.perm.info.packageName = tree.perm.info.packageName; @@ -3178,28 +4029,48 @@ class PackageManagerService extends IPackageManager.Stub { } } } - if (bp.perm == null) { - Log.w(TAG, "Removing dangling permission: " + bp.name + if (bp.packageSetting == null) { + // We may not yet have parsed the package, so just see if + // we still know about its settings. + bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage); + } + if (bp.packageSetting == null) { + Slog.w(TAG, "Removing dangling permission: " + bp.name + " from package " + bp.sourcePackage); it.remove(); + } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) { + if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) { + Slog.i(TAG, "Removing old permission: " + bp.name + + " from package " + bp.sourcePackage); + grantPermissions = true; + it.remove(); + } } } // Now update the permissions for all packages, in particular // replace the granted permissions of the system packages. - for (PackageParser.Package pkg : mPackages.values()) { - grantPermissionsLP(pkg, false); + if (grantPermissions) { + for (PackageParser.Package pkg : mPackages.values()) { + if (pkg != pkgInfo) { + grantPermissionsLP(pkg, replaceAll); + } + } + } + + if (pkgInfo != null) { + grantPermissionsLP(pkgInfo, replace); } } - + private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) { final PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps == null) { return; } final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; - boolean addedPermission = false; - + boolean changedPermission = false; + if (replace) { ps.permissionsFixed = false; if (gp == ps) { @@ -3207,39 +4078,42 @@ class PackageManagerService extends IPackageManager.Stub { gp.gids = mGlobalGids; } } - + if (gp.gids == null) { gp.gids = mGlobalGids; } - + final int N = pkg.requestedPermissions.size(); for (int i=0; i<N; i++) { String name = pkg.requestedPermissions.get(i); BasePermission bp = mSettings.mPermissions.get(name); - PackageParser.Permission p = bp != null ? bp.perm : null; if (false) { if (gp != ps) { Log.i(TAG, "Package " + pkg.packageName + " checking " + name - + ": " + p); + + ": " + bp); } } - if (p != null) { - final String perm = p.info.name; + if (bp != null && bp.packageSetting != null) { + final String perm = bp.name; boolean allowed; - if (p.info.protectionLevel == PermissionInfo.PROTECTION_NORMAL - || p.info.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) { + boolean allowedSig = false; + if (bp.protectionLevel == PermissionInfo.PROTECTION_NORMAL + || bp.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) { allowed = true; - } else if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE - || p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { - allowed = (checkSignaturesLP(p.owner.mSignatures, pkg.mSignatures) + } else if (bp.packageSetting == null) { + // This permission is invalid; skip it. + allowed = false; + } else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE + || bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { + allowed = (checkSignaturesLP(bp.packageSetting.signatures.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH) || (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH); - if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { + if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { // For updated system applications, the signatureOrSystem permission // is granted only if it had been defined by the original application. - if ((pkg.applicationInfo.flags + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { PackageSetting sysPs = mSettings.getDisabledSystemPkg(pkg.packageName); if(sysPs.grantedPermissions.contains(perm)) { @@ -3252,6 +4126,9 @@ class PackageManagerService extends IPackageManager.Stub { } } } + if (allowed) { + allowedSig = true; + } } else { allowed = false; } @@ -3265,7 +4142,7 @@ class PackageManagerService extends IPackageManager.Stub { && ps.permissionsFixed) { // If this is an existing, non-system package, then // we can't add any new permissions to it. - if (!gp.loadedPermissions.contains(perm)) { + if (!allowedSig && !gp.grantedPermissions.contains(perm)) { allowed = false; // Except... if this is a permission that was added // to the platform (note: need to only do this when @@ -3277,7 +4154,7 @@ class PackageManagerService extends IPackageManager.Stub { if (npi.name.equals(perm) && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) { allowed = true; - Log.i(TAG, "Auto-granting WRITE_EXTERNAL_STORAGE to old pkg " + Log.i(TAG, "Auto-granting " + perm + " to old pkg " + pkg.packageName); break; } @@ -3286,39 +4163,51 @@ class PackageManagerService extends IPackageManager.Stub { } if (allowed) { if (!gp.grantedPermissions.contains(perm)) { - addedPermission = true; + changedPermission = true; gp.grantedPermissions.add(perm); gp.gids = appendInts(gp.gids, bp.gids); + } else if (!ps.haveGids) { + gp.gids = appendInts(gp.gids, bp.gids); } } else { - Log.w(TAG, "Not granting permission " + perm + Slog.w(TAG, "Not granting permission " + perm + " to package " + pkg.packageName + " because it was previously installed without"); } } else { - Log.w(TAG, "Not granting permission " + perm - + " to package " + pkg.packageName - + " (protectionLevel=" + p.info.protectionLevel - + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) - + ")"); + if (gp.grantedPermissions.remove(perm)) { + changedPermission = true; + gp.gids = removeInts(gp.gids, bp.gids); + Slog.i(TAG, "Un-granting permission " + perm + + " from package " + pkg.packageName + + " (protectionLevel=" + bp.protectionLevel + + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + + ")"); + } else { + Slog.w(TAG, "Not granting permission " + perm + + " to package " + pkg.packageName + + " (protectionLevel=" + bp.protectionLevel + + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + + ")"); + } } } else { - Log.w(TAG, "Unknown permission " + name + Slog.w(TAG, "Unknown permission " + name + " in package " + pkg.packageName); } } - - if ((addedPermission || replace) && !ps.permissionsFixed && + + if ((changedPermission || replace) && !ps.permissionsFixed && ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) || ((ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)){ // This is the first that we have heard about this package, so the // permissions we have now selected are fixed until explicitly // changed. ps.permissionsFixed = true; - gp.loadedPermissions = new HashSet<String>(gp.grantedPermissions); } + ps.haveGids = true; } - + private final class ActivityIntentResolver extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> { public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { @@ -3354,7 +4243,7 @@ class PackageManagerService extends IPackageManager.Stub { } public final void addActivity(PackageParser.Activity a, String type) { - mActivities.put(a.component, a); + mActivities.put(a.getComponentName(), a); if (SHOW_INFO || Config.LOGV) Log.v( TAG, " " + type + " " + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); @@ -3374,7 +4263,7 @@ class PackageManagerService extends IPackageManager.Stub { } public final void removeActivity(PackageParser.Activity a, String type) { - mActivities.remove(a.component); + mActivities.remove(a.getComponentName()); if (SHOW_INFO || Config.LOGV) Log.v( TAG, " " + type + " " + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); @@ -3403,6 +4292,11 @@ class PackageManagerService extends IPackageManager.Stub { } return true; } + + @Override + protected String packageForFilter(PackageParser.ActivityIntentInfo info) { + return info.activity.owner.packageName; + } @Override protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info, @@ -3444,7 +4338,9 @@ class PackageManagerService extends IPackageManager.Stub { out.print(prefix); out.print( Integer.toHexString(System.identityHashCode(filter.activity))); out.print(' '); - out.println(filter.activity.componentShortName); + out.print(filter.activity.getComponentShortName()); + out.print(" filter "); + out.println(Integer.toHexString(System.identityHashCode(filter))); } // List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) { @@ -3500,7 +4396,7 @@ class PackageManagerService extends IPackageManager.Stub { } public final void addService(PackageParser.Service s) { - mServices.put(s.component, s); + mServices.put(s.getComponentName(), s); if (SHOW_INFO || Config.LOGV) Log.v( TAG, " " + (s.info.nonLocalizedLabel != null ? s.info.nonLocalizedLabel : s.info.name) + ":"); @@ -3522,7 +4418,7 @@ class PackageManagerService extends IPackageManager.Stub { } public final void removeService(PackageParser.Service s) { - mServices.remove(s.component); + mServices.remove(s.getComponentName()); if (SHOW_INFO || Config.LOGV) Log.v( TAG, " " + (s.info.nonLocalizedLabel != null ? s.info.nonLocalizedLabel : s.info.name) + ":"); @@ -3553,6 +4449,11 @@ class PackageManagerService extends IPackageManager.Stub { } return true; } + + @Override + protected String packageForFilter(PackageParser.ServiceIntentInfo info) { + return info.service.owner.packageName; + } @Override protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter, @@ -3595,7 +4496,9 @@ class PackageManagerService extends IPackageManager.Stub { out.print(prefix); out.print( Integer.toHexString(System.identityHashCode(filter.service))); out.print(' '); - out.println(filter.service.componentShortName); + out.print(filter.service.getComponentShortName()); + out.print(" filter "); + out.println(Integer.toHexString(System.identityHashCode(filter))); } // List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) { @@ -3649,7 +4552,8 @@ class PackageManagerService extends IPackageManager.Stub { } }; - private static final void sendPackageBroadcast(String action, String pkg, Bundle extras) { + private static final void sendPackageBroadcast(String action, String pkg, + Bundle extras, IIntentReceiver finishedReceiver) { IActivityManager am = ActivityManagerNative.getDefault(); if (am != null) { try { @@ -3659,14 +4563,53 @@ class PackageManagerService extends IPackageManager.Stub { intent.putExtras(extras); } intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - am.broadcastIntent( - null, intent, - null, null, 0, null, null, null, false, false); + am.broadcastIntent(null, intent, null, finishedReceiver, + 0, null, null, null, finishedReceiver != null, false); } catch (RemoteException ex) { } } } + + public String nextPackageToClean(String lastPackage) { + synchronized (mPackages) { + if (!mMediaMounted) { + // If the external storage is no longer mounted at this point, + // the caller may not have been able to delete all of this + // packages files and can not delete any more. Bail. + return null; + } + if (lastPackage != null) { + mSettings.mPackagesToBeCleaned.remove(lastPackage); + } + return mSettings.mPackagesToBeCleaned.size() > 0 + ? mSettings.mPackagesToBeCleaned.get(0) : null; + } + } + void schedulePackageCleaning(String packageName) { + mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName)); + } + + void startCleaningPackages() { + synchronized (mPackages) { + if (!mMediaMounted) { + return; + } + if (mSettings.mPackagesToBeCleaned.size() <= 0) { + return; + } + } + Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE); + intent.setComponent(DEFAULT_CONTAINER_COMPONENT); + IActivityManager am = ActivityManagerNative.getDefault(); + if (am != null) { + try { + am.startService(null, intent, null); + } catch (RemoteException e) { + } + } + } + private final class AppDirObserver extends FileObserver { public AppDirObserver(String path, int mask, boolean isrom) { super(path, mask); @@ -3698,28 +4641,35 @@ class PackageManagerService extends IPackageManager.Stub { return; } + // Ignore packages that are being installed or + // have just been installed. + if (ignoreCodePath(fullPathStr)) { + return; + } + PackageParser.Package p = null; + synchronized (mPackages) { + p = mAppDirs.get(fullPathStr); + } if ((event&REMOVE_EVENTS) != 0) { - synchronized (mInstallLock) { - PackageParser.Package p = mAppDirs.get(fullPathStr); - if (p != null) { - removePackageLI(p, true); - removedPackage = p.applicationInfo.packageName; - removedUid = p.applicationInfo.uid; - } + if (p != null) { + removePackageLI(p, true); + removedPackage = p.applicationInfo.packageName; + removedUid = p.applicationInfo.uid; } } if ((event&ADD_EVENTS) != 0) { - PackageParser.Package p = mAppDirs.get(fullPathStr); if (p == null) { - p = scanPackageLI(fullPath, fullPath, fullPath, - (mIsRom ? PackageParser.PARSE_IS_SYSTEM : 0) | + p = scanPackageLI(fullPath, + (mIsRom ? PackageParser.PARSE_IS_SYSTEM + | PackageParser.PARSE_IS_SYSTEM_DIR: 0) | PackageParser.PARSE_CHATTY | PackageParser.PARSE_MUST_BE_APK, - SCAN_MONITOR); + SCAN_MONITOR | SCAN_NO_PATHS); if (p != null) { synchronized (mPackages) { - grantPermissionsLP(p, false); + updatePermissionsLP(p.packageName, p, + p.permissions.size() > 0, false, false); } addedPackage = p.applicationInfo.packageName; addedUid = p.applicationInfo.uid; @@ -3736,12 +4686,14 @@ class PackageManagerService extends IPackageManager.Stub { 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); + sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, + extras, null); } if (addedPackage != null) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, addedUid); - sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage, extras); + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage, + extras, null); } } @@ -3754,60 +4706,845 @@ class PackageManagerService extends IPackageManager.Stub { final Uri packageURI, final IPackageInstallObserver observer, final int flags) { installPackage(packageURI, observer, flags, null); } - + /* Called when a downloaded package installation has been confirmed by the user */ public void installPackage( final Uri packageURI, final IPackageInstallObserver observer, final int flags, final String installerPackageName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INSTALL_PACKAGES, null); - + + Message msg = mHandler.obtainMessage(INIT_COPY); + msg.obj = new InstallParams(packageURI, observer, flags, + installerPackageName); + mHandler.sendMessage(msg); + } + + public void finishPackageInstall(int token) { + if (DEBUG_INSTALL) Log.v(TAG, "BM finishing package install for " + token); + Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0); + mHandler.sendMessage(msg); + } + + private void processPendingInstall(final InstallArgs args, final int currentStatus) { // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); // Result object to be returned PackageInstalledInfo res = new PackageInstalledInfo(); - res.returnCode = PackageManager.INSTALL_SUCCEEDED; + res.returnCode = currentStatus; res.uid = -1; res.pkg = null; res.removedInfo = new PackageRemovedInfo(); - // Make a temporary copy of file from given packageURI - File tmpPackageFile = copyTempInstallFile(packageURI, res); - if (tmpPackageFile != null) { + if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { + args.doPreInstall(res.returnCode); synchronized (mInstallLock) { - installPackageLI(packageURI, flags, true, installerPackageName, tmpPackageFile, res); + installPackageLI(args, true, res); + } + args.doPostInstall(res.returnCode); + } + + // A restore should be performed at this point if (a) the install + // succeeded, (b) the operation is not an update, and (c) the new + // package has a backupAgent defined. + final boolean update = res.removedInfo.removedPackage != null; + boolean doRestore = (!update + && res.pkg != null + && res.pkg.applicationInfo.backupAgentName != null); + + // Set up the post-install work request bookkeeping. This will be used + // and cleaned up by the post-install event handling regardless of whether + // there's a restore pass performed. Token values are >= 1. + int token; + if (mNextInstallToken < 0) mNextInstallToken = 1; + token = mNextInstallToken++; + + PostInstallData data = new PostInstallData(args, res); + mRunningInstalls.put(token, data); + if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token); + + if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) { + // Pass responsibility to the Backup Manager. It will perform a + // restore if appropriate, then pass responsibility back to the + // Package Manager to run the post-install observer callbacks + // and broadcasts. + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + if (bm != null) { + if (DEBUG_INSTALL) Log.v(TAG, "token " + token + + " to BM for possible restore"); + try { + bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token); + } catch (RemoteException e) { + // can't happen; the backup manager is local + } catch (Exception e) { + Slog.e(TAG, "Exception trying to enqueue restore", e); + doRestore = false; + } + } else { + Slog.e(TAG, "Backup Manager not found!"); + doRestore = false; } } - if (observer != null) { - try { - observer.packageInstalled(res.name, res.returnCode); - } catch (RemoteException e) { - Log.i(TAG, "Observer no longer exists."); + + if (!doRestore) { + // No restore possible, or the Backup Manager was mysteriously not + // available -- just fire the post-install work request directly. + if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token); + Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0); + mHandler.sendMessage(msg); + } + } + }); + } + + abstract class HandlerParams { + final static int MAX_RETRIES = 4; + int retry = 0; + final void startCopy() { + try { + if (DEBUG_SD_INSTALL) Log.i(TAG, "startCopy"); + retry++; + if (retry > MAX_RETRIES) { + Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); + mHandler.sendEmptyMessage(MCS_GIVE_UP); + handleServiceError(); + return; + } else { + handleStartCopy(); + if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting install MCS_UNBIND"); + mHandler.sendEmptyMessage(MCS_UNBIND); + } + } catch (RemoteException e) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting install MCS_RECONNECT"); + mHandler.sendEmptyMessage(MCS_RECONNECT); + } + handleReturnCode(); + } + + final void serviceError() { + if (DEBUG_SD_INSTALL) Log.i(TAG, "serviceError"); + handleServiceError(); + handleReturnCode(); + } + abstract void handleStartCopy() throws RemoteException; + abstract void handleServiceError(); + abstract void handleReturnCode(); + } + + class InstallParams extends HandlerParams { + final IPackageInstallObserver observer; + int flags; + final Uri packageURI; + final String installerPackageName; + private InstallArgs mArgs; + private int mRet; + InstallParams(Uri packageURI, + IPackageInstallObserver observer, int flags, + String installerPackageName) { + this.packageURI = packageURI; + this.flags = flags; + this.observer = observer; + this.installerPackageName = installerPackageName; + } + + private int installLocationPolicy(PackageInfoLite pkgLite, int flags) { + String packageName = pkgLite.packageName; + int installLocation = pkgLite.installLocation; + boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0; + synchronized (mPackages) { + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg != null) { + if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { + // Check for updated system application. + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + if (onSd) { + Slog.w(TAG, "Cannot install update to system app on sdcard"); + return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION; + } + return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + } else { + if (onSd) { + // Install flag overrides everything. + return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + } + // If current upgrade specifies particular preference + if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { + // Application explicitly specified internal. + return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { + // App explictly prefers external. Let policy decide + } else { + // Prefer previous location + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { + return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + } + return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + } + } + } else { + // Invalid install. Return error code + return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS; } } - // 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(false, true); - Bundle extras = new Bundle(1); - extras.putInt(Intent.EXTRA_UID, res.uid); - final boolean update = res.removedInfo.removedPackage != null; - if (update) { - extras.putBoolean(Intent.EXTRA_REPLACING, true); + } + // All the special cases have been taken care of. + // Return result based on recommended install location. + if (onSd) { + return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + } + return pkgLite.recommendedInstallLocation; + } + + /* + * Invoke remote method to get package information and install + * location values. Override install location based on default + * policy if needed and then create install arguments based + * on the install location. + */ + public void handleStartCopy() throws RemoteException { + int ret = PackageManager.INSTALL_SUCCEEDED; + boolean fwdLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0; + boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0; + boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0; + if (onInt && onSd) { + // Check if both bits are set. + Slog.w(TAG, "Conflicting flags specified for installing on both internal and external"); + ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; + } else if (fwdLocked && onSd) { + // Check for forward locked apps + Slog.w(TAG, "Cannot install fwd locked apps on sdcard"); + ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; + } else { + // Remote call to find out default install location + PackageInfoLite pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags); + int loc = pkgLite.recommendedInstallLocation; + if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION){ + ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; + } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS){ + ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; + } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){ + ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) { + ret = PackageManager.INSTALL_FAILED_INVALID_APK; + } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) { + ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; + } else { + // Override with defaults if needed. + loc = installLocationPolicy(pkgLite, flags); + if (!onSd && !onInt) { + // Override install location with flags + if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { + // Set the flag to install on external media. + flags |= PackageManager.INSTALL_EXTERNAL; + flags &= ~PackageManager.INSTALL_INTERNAL; + } else { + // Make sure the flag for installing on external + // media is unset + flags |= PackageManager.INSTALL_INTERNAL; + flags &= ~PackageManager.INSTALL_EXTERNAL; + } } - sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, - res.pkg.applicationInfo.packageName, - extras); - if (update) { - sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, - res.pkg.applicationInfo.packageName, - extras); + } + } + // Create the file args now. + mArgs = createInstallArgs(this); + if (ret == PackageManager.INSTALL_SUCCEEDED) { + // Create copy only if we are not in an erroneous state. + // Remote call to initiate copy using temporary file + ret = mArgs.copyApk(mContainerService, true); + } + mRet = ret; + } + + @Override + void handleReturnCode() { + processPendingInstall(mArgs, mRet); + } + + @Override + void handleServiceError() { + mArgs = createInstallArgs(this); + mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + } + } + + /* + * Utility class used in movePackage api. + * srcArgs and targetArgs are not set for invalid flags and make + * sure to do null checks when invoking methods on them. + * We probably want to return ErrorPrams for both failed installs + * and moves. + */ + class MoveParams extends HandlerParams { + final IPackageMoveObserver observer; + final int flags; + final String packageName; + final InstallArgs srcArgs; + final InstallArgs targetArgs; + int mRet; + MoveParams(InstallArgs srcArgs, + IPackageMoveObserver observer, + int flags, String packageName) { + this.srcArgs = srcArgs; + this.observer = observer; + this.flags = flags; + this.packageName = packageName; + if (srcArgs != null) { + Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath())); + targetArgs = createInstallArgs(packageUri, flags, packageName); + } else { + targetArgs = null; + } + } + + public void handleStartCopy() throws RemoteException { + mRet = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + // Check for storage space on target medium + if (!targetArgs.checkFreeStorage(mContainerService)) { + Log.w(TAG, "Insufficient storage to install"); + return; + } + // Create the file args now. + mRet = targetArgs.copyApk(mContainerService, false); + targetArgs.doPreInstall(mRet); + if (DEBUG_SD_INSTALL) { + StringBuilder builder = new StringBuilder(); + if (srcArgs != null) { + builder.append("src: "); + builder.append(srcArgs.getCodePath()); + } + if (targetArgs != null) { + builder.append(" target : "); + builder.append(targetArgs.getCodePath()); + } + Log.i(TAG, builder.toString()); + } + } + + @Override + void handleReturnCode() { + targetArgs.doPostInstall(mRet); + int currentStatus = PackageManager.MOVE_FAILED_INTERNAL_ERROR; + if (mRet == PackageManager.INSTALL_SUCCEEDED) { + currentStatus = PackageManager.MOVE_SUCCEEDED; + } else if (mRet == PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE){ + currentStatus = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; + } + processPendingMove(this, currentStatus); + } + + @Override + void handleServiceError() { + mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + } + } + + private InstallArgs createInstallArgs(InstallParams params) { + if (installOnSd(params.flags)) { + return new SdInstallArgs(params); + } else { + return new FileInstallArgs(params); + } + } + + private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath) { + if (installOnSd(flags)) { + return new SdInstallArgs(fullCodePath, fullResourcePath); + } else { + return new FileInstallArgs(fullCodePath, fullResourcePath); + } + } + + private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName) { + if (installOnSd(flags)) { + String cid = getNextCodePath(null, pkgName, "/" + SdInstallArgs.RES_FILE_NAME); + return new SdInstallArgs(packageURI, cid); + } else { + return new FileInstallArgs(packageURI, pkgName); + } + } + + static abstract class InstallArgs { + final IPackageInstallObserver observer; + // Always refers to PackageManager flags only + final int flags; + final Uri packageURI; + final String installerPackageName; + + InstallArgs(Uri packageURI, + IPackageInstallObserver observer, int flags, + String installerPackageName) { + this.packageURI = packageURI; + this.flags = flags; + this.observer = observer; + this.installerPackageName = installerPackageName; + } + + abstract void createCopyFile(); + abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException; + abstract int doPreInstall(int status); + abstract boolean doRename(int status, String pkgName, String oldCodePath); + abstract int doPostInstall(int status); + abstract String getCodePath(); + abstract String getResourcePath(); + // Need installer lock especially for dex file removal. + abstract void cleanUpResourcesLI(); + abstract boolean doPostDeleteLI(boolean delete); + abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException; + } + + class FileInstallArgs extends InstallArgs { + File installDir; + String codeFileName; + String resourceFileName; + boolean created = false; + + FileInstallArgs(InstallParams params) { + super(params.packageURI, params.observer, + params.flags, params.installerPackageName); + } + + FileInstallArgs(String fullCodePath, String fullResourcePath) { + super(null, null, 0, null); + File codeFile = new File(fullCodePath); + installDir = codeFile.getParentFile(); + codeFileName = fullCodePath; + resourceFileName = fullResourcePath; + } + + FileInstallArgs(Uri packageURI, String pkgName) { + super(packageURI, null, 0, null); + boolean fwdLocked = isFwdLocked(flags); + installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir; + String apkName = getNextCodePath(null, pkgName, ".apk"); + codeFileName = new File(installDir, apkName + ".apk").getPath(); + resourceFileName = getResourcePathFromCodePath(); + } + + boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { + return imcs.checkFreeStorage(false, packageURI); + } + + String getCodePath() { + return codeFileName; + } + + void createCopyFile() { + boolean fwdLocked = isFwdLocked(flags); + installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir; + codeFileName = createTempPackageFile(installDir).getPath(); + resourceFileName = getResourcePathFromCodePath(); + created = true; + } + + int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { + if (temp) { + // Generate temp file name + createCopyFile(); + } + // Get a ParcelFileDescriptor to write to the output file + File codeFile = new File(codeFileName); + if (!created) { + try { + codeFile.createNewFile(); + // Set permissions + if (!setPermissions()) { + // Failed setting permissions. + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } + } catch (IOException e) { + Slog.w(TAG, "Failed to create file " + codeFile); + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } - Runtime.getRuntime().gc(); } - }); + ParcelFileDescriptor out = null; + try { + out = ParcelFileDescriptor.open(codeFile, + ParcelFileDescriptor.MODE_READ_WRITE); + } catch (FileNotFoundException e) { + Slog.e(TAG, "Failed to create file descritpor for : " + codeFileName); + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } + // Copy the resource now + int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + try { + if (imcs.copyResource(packageURI, out)) { + ret = PackageManager.INSTALL_SUCCEEDED; + } + } finally { + try { if (out != null) out.close(); } catch (IOException e) {} + } + return ret; + } + + int doPreInstall(int status) { + if (status != PackageManager.INSTALL_SUCCEEDED) { + cleanUp(); + } + return status; + } + + boolean doRename(int status, final String pkgName, String oldCodePath) { + if (status != PackageManager.INSTALL_SUCCEEDED) { + cleanUp(); + return false; + } else { + // Rename based on packageName + File codeFile = new File(getCodePath()); + String apkName = getNextCodePath(oldCodePath, pkgName, ".apk"); + File desFile = new File(installDir, apkName + ".apk"); + if (!codeFile.renameTo(desFile)) { + return false; + } + // Reset paths since the file has been renamed. + codeFileName = desFile.getPath(); + resourceFileName = getResourcePathFromCodePath(); + // Set permissions + if (!setPermissions()) { + // Failed setting permissions. + return false; + } + return true; + } + } + + int doPostInstall(int status) { + if (status != PackageManager.INSTALL_SUCCEEDED) { + cleanUp(); + } + return status; + } + + String getResourcePath() { + return resourceFileName; + } + + String getResourcePathFromCodePath() { + String codePath = getCodePath(); + if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) { + String apkNameOnly = getApkName(codePath); + return mAppInstallDir.getPath() + "/" + apkNameOnly + ".zip"; + } else { + return codePath; + } + } + + private boolean cleanUp() { + boolean ret = true; + String sourceDir = getCodePath(); + String publicSourceDir = getResourcePath(); + if (sourceDir != null) { + File sourceFile = new File(sourceDir); + if (!sourceFile.exists()) { + Slog.w(TAG, "Package source " + sourceDir + " does not exist."); + ret = false; + } + // Delete application's code and resources + sourceFile.delete(); + } + if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) { + final File publicSourceFile = new File(publicSourceDir); + if (!publicSourceFile.exists()) { + Slog.w(TAG, "Package public source " + publicSourceFile + " does not exist."); + } + if (publicSourceFile.exists()) { + publicSourceFile.delete(); + } + } + return ret; + } + + void cleanUpResourcesLI() { + String sourceDir = getCodePath(); + if (cleanUp() && mInstaller != null) { + int retCode = mInstaller.rmdex(sourceDir); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove dex file for package: " + + " at location " + + sourceDir + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion + } + } + } + + private boolean setPermissions() { + // TODO Do this in a more elegant way later on. for now just a hack + if (!isFwdLocked(flags)) { + final int filePermissions = + FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP + |FileUtils.S_IROTH; + int retCode = FileUtils.setPermissions(getCodePath(), filePermissions, -1, -1); + if (retCode != 0) { + Slog.e(TAG, "Couldn't set new package file permissions for " + + getCodePath() + + ". The return code was: " + retCode); + // TODO Define new internal error + return false; + } + return true; + } + return true; + } + + boolean doPostDeleteLI(boolean delete) { + cleanUpResourcesLI(); + return true; + } + } + + class SdInstallArgs extends InstallArgs { + String cid; + String cachePath; + static final String RES_FILE_NAME = "pkg.apk"; + + SdInstallArgs(InstallParams params) { + super(params.packageURI, params.observer, + params.flags, params.installerPackageName); + } + + SdInstallArgs(String fullCodePath, String fullResourcePath) { + super(null, null, PackageManager.INSTALL_EXTERNAL, null); + // Extract cid from fullCodePath + int eidx = fullCodePath.lastIndexOf("/"); + String subStr1 = fullCodePath.substring(0, eidx); + int sidx = subStr1.lastIndexOf("/"); + cid = subStr1.substring(sidx+1, eidx); + cachePath = subStr1; + } + + SdInstallArgs(String cid) { + super(null, null, PackageManager.INSTALL_EXTERNAL, null); + this.cid = cid; + } + + SdInstallArgs(Uri packageURI, String cid) { + super(packageURI, null, PackageManager.INSTALL_EXTERNAL, null); + this.cid = cid; + } + + void createCopyFile() { + cid = getTempContainerId(); + } + + boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { + return imcs.checkFreeStorage(true, packageURI); + } + + int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { + if (temp) { + createCopyFile(); + } + cachePath = imcs.copyResourceToContainer( + packageURI, cid, + getEncryptKey(), RES_FILE_NAME); + return (cachePath == null) ? PackageManager.INSTALL_FAILED_CONTAINER_ERROR : + PackageManager.INSTALL_SUCCEEDED; + } + + @Override + String getCodePath() { + return cachePath + "/" + RES_FILE_NAME; + } + + @Override + String getResourcePath() { + return cachePath + "/" + RES_FILE_NAME; + } + + int doPreInstall(int status) { + if (status != PackageManager.INSTALL_SUCCEEDED) { + // Destroy container + PackageHelper.destroySdDir(cid); + } else { + boolean mounted = PackageHelper.isContainerMounted(cid); + if (!mounted) { + cachePath = PackageHelper.mountSdDir(cid, getEncryptKey(), Process.SYSTEM_UID); + if (cachePath == null) { + return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; + } + } + } + return status; + } + + boolean doRename(int status, final String pkgName, + String oldCodePath) { + String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME); + String newCachePath = null; + if (PackageHelper.isContainerMounted(cid)) { + // Unmount the container + if (!PackageHelper.unMountSdDir(cid)) { + Slog.i(TAG, "Failed to unmount " + cid + " before renaming"); + return false; + } + } + if (!PackageHelper.renameSdDir(cid, newCacheId)) { + Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId + + " which might be stale. Will try to clean up."); + // Clean up the stale container and proceed to recreate. + if (!PackageHelper.destroySdDir(newCacheId)) { + Slog.e(TAG, "Very strange. Cannot clean up stale container " + newCacheId); + return false; + } + // Successfully cleaned up stale container. Try to rename again. + if (!PackageHelper.renameSdDir(cid, newCacheId)) { + Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId + + " inspite of cleaning it up."); + return false; + } + } + if (!PackageHelper.isContainerMounted(newCacheId)) { + Slog.w(TAG, "Mounting container " + newCacheId); + newCachePath = PackageHelper.mountSdDir(newCacheId, + getEncryptKey(), Process.SYSTEM_UID); + } else { + newCachePath = PackageHelper.getSdDir(newCacheId); + } + if (newCachePath == null) { + Slog.w(TAG, "Failed to get cache path for " + newCacheId); + return false; + } + Log.i(TAG, "Succesfully renamed " + cid + + " at path: " + cachePath + " to " + newCacheId + + " at new path: " + newCachePath); + cid = newCacheId; + cachePath = newCachePath; + return true; + } + + int doPostInstall(int status) { + if (status != PackageManager.INSTALL_SUCCEEDED) { + cleanUp(); + } else { + boolean mounted = PackageHelper.isContainerMounted(cid); + if (!mounted) { + PackageHelper.mountSdDir(cid, + getEncryptKey(), Process.myUid()); + } + } + return status; + } + + private void cleanUp() { + // Destroy secure container + PackageHelper.destroySdDir(cid); + } + + void cleanUpResourcesLI() { + String sourceFile = getCodePath(); + // Remove dex file + if (mInstaller != null) { + int retCode = mInstaller.rmdex(sourceFile); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove dex file for package: " + + " at location " + + sourceFile.toString() + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion + } + } + cleanUp(); + } + + boolean matchContainer(String app) { + if (cid.startsWith(app)) { + return true; + } + return false; + } + + String getPackageName() { + int idx = cid.lastIndexOf("-"); + if (idx == -1) { + return cid; + } + return cid.substring(0, idx); + } + + boolean doPostDeleteLI(boolean delete) { + boolean ret = false; + boolean mounted = PackageHelper.isContainerMounted(cid); + if (mounted) { + // Unmount first + ret = PackageHelper.unMountSdDir(cid); + } + if (ret && delete) { + cleanUpResourcesLI(); + } + return ret; + } + }; + + // Utility method used to create code paths based on package name and available index. + private static String getNextCodePath(String oldCodePath, String prefix, String suffix) { + String idxStr = ""; + int idx = 1; + // Fall back to default value of idx=1 if prefix is not + // part of oldCodePath + if (oldCodePath != null) { + String subStr = oldCodePath; + // Drop the suffix right away + if (subStr.endsWith(suffix)) { + subStr = subStr.substring(0, subStr.length() - suffix.length()); + } + // If oldCodePath already contains prefix find out the + // ending index to either increment or decrement. + int sidx = subStr.lastIndexOf(prefix); + if (sidx != -1) { + subStr = subStr.substring(sidx + prefix.length()); + if (subStr != null) { + if (subStr.startsWith(INSTALL_PACKAGE_SUFFIX)) { + subStr = subStr.substring(INSTALL_PACKAGE_SUFFIX.length()); + } + try { + idx = Integer.parseInt(subStr); + if (idx <= 1) { + idx++; + } else { + idx--; + } + } catch(NumberFormatException e) { + } + } + } + } + idxStr = INSTALL_PACKAGE_SUFFIX + Integer.toString(idx); + return prefix + idxStr; + } + + // Utility method used to ignore ADD/REMOVE events + // by directory observer. + private static boolean ignoreCodePath(String fullPathStr) { + String apkName = getApkName(fullPathStr); + int idx = apkName.lastIndexOf(INSTALL_PACKAGE_SUFFIX); + if (idx != -1 && ((idx+1) < apkName.length())) { + // Make sure the package ends with a numeral + String version = apkName.substring(idx+1); + try { + Integer.parseInt(version); + return true; + } catch (NumberFormatException e) {} + } + return false; + } + + // Utility method that returns the relative package path with respect + // to the installation directory. Like say for /data/data/com.test-1.apk + // string com.test-1 is returned. + static String getApkName(String codePath) { + if (codePath == null) { + return null; + } + int sidx = codePath.lastIndexOf("/"); + int eidx = codePath.lastIndexOf("."); + if (eidx == -1) { + eidx = codePath.length(); + } else if (eidx == 0) { + Slog.w(TAG, " Invalid code path, "+ codePath + " Not a valid apk name"); + return null; + } + return codePath.substring(sidx+1, eidx); } class PackageInstalledInfo { @@ -3817,51 +5554,48 @@ class PackageManagerService extends IPackageManager.Stub { int returnCode; PackageRemovedInfo removedInfo; } - + /* * Install a non-existing package. */ - private void installNewPackageLI(String pkgName, - File tmpPackageFile, - String destFilePath, File destPackageFile, File destResourceFile, - PackageParser.Package pkg, boolean forwardLocked, boolean newInstall, + private void installNewPackageLI(PackageParser.Package pkg, + int parseFlags, + int scanMode, String installerPackageName, PackageInstalledInfo res) { // Remember this for later, in case we need to rollback this install - boolean dataDirExists = (new File(mAppDataDir, pkgName)).exists(); + String pkgName = pkg.packageName; + + boolean dataDirExists = getDataPathForPackage(pkg).exists(); res.name = pkgName; synchronized(mPackages) { - if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(destFilePath)) { + if (mSettings.mRenamedPackages.containsKey(pkgName)) { + // A package with the same name is already installed, though + // it has been renamed to an older name. The package we + // are trying to install should be installed as an update to + // the existing one, but that has not been requested, so bail. + Slog.w(TAG, "Attempt to re-install " + pkgName + + " without first uninstalling package running as " + + mSettings.mRenamedPackages.get(pkgName)); + res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; + return; + } + if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) { // Don't allow installation over an existing package with the same name. - Log.w(TAG, "Attempt to re-install " + pkgName + Slog.w(TAG, "Attempt to re-install " + pkgName + " without first uninstalling."); res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; return; } } - if (destPackageFile.exists()) { - // It's safe to do this because we know (from the above check) that the file - // isn't currently used for an installed package. - destPackageFile.delete(); - } mLastScanError = PackageManager.INSTALL_SUCCEEDED; - PackageParser.Package newPackage = scanPackageLI(tmpPackageFile, destPackageFile, - destResourceFile, pkg, 0, - SCAN_MONITOR | SCAN_FORCE_DEX - | SCAN_UPDATE_SIGNATURE - | (forwardLocked ? SCAN_FORWARD_LOCKED : 0) - | (newInstall ? SCAN_NEW_INSTALL : 0)); + PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode); if (newPackage == null) { - Log.w(TAG, "Package couldn't be installed in " + destPackageFile); + Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; } } else { - updateSettingsLI(pkgName, tmpPackageFile, - destFilePath, destPackageFile, - destResourceFile, pkg, - newPackage, - true, - forwardLocked, + updateSettingsLI(newPackage, installerPackageName, res); // delete the partially installed application. the data directory will have to be @@ -3872,60 +5606,53 @@ class PackageManagerService extends IPackageManager.Stub { // scanPackageLocked, unless those directories existed before we even tried to // install. deletePackageLI( - pkgName, true, + pkgName, false, dataDirExists ? PackageManager.DONT_DELETE_DATA : 0, res.removedInfo); } } } - - private void replacePackageLI(String pkgName, - File tmpPackageFile, - String destFilePath, File destPackageFile, File destResourceFile, - PackageParser.Package pkg, boolean forwardLocked, boolean newInstall, + + private void replacePackageLI(PackageParser.Package pkg, + int parseFlags, + int scanMode, String installerPackageName, PackageInstalledInfo res) { PackageParser.Package oldPackage; + String pkgName = pkg.packageName; // First find the old package info and check signatures synchronized(mPackages) { oldPackage = mPackages.get(pkgName); - if(checkSignaturesLP(pkg.mSignatures, oldPackage.mSignatures) + if (checkSignaturesLP(oldPackage.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; return; } } boolean sysPkg = ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); - if(sysPkg) { - replaceSystemPackageLI(oldPackage, - tmpPackageFile, destFilePath, - destPackageFile, destResourceFile, pkg, forwardLocked, - newInstall, installerPackageName, res); + if (sysPkg) { + replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res); } else { - replaceNonSystemPackageLI(oldPackage, tmpPackageFile, destFilePath, - destPackageFile, destResourceFile, pkg, forwardLocked, - newInstall, installerPackageName, res); + replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res); } } - + private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, - File tmpPackageFile, - String destFilePath, File destPackageFile, File destResourceFile, - PackageParser.Package pkg, boolean forwardLocked, boolean newInstall, + PackageParser.Package pkg, + int parseFlags, int scanMode, String installerPackageName, PackageInstalledInfo res) { PackageParser.Package newPackage = null; String pkgName = deletedPackage.packageName; boolean deletedPkg = true; boolean updatedSettings = false; - + String oldInstallerPackageName = null; synchronized (mPackages) { oldInstallerPackageName = mSettings.getInstallerPackageName(pkgName); } - - int parseFlags = PackageManager.INSTALL_REPLACE_EXISTING; + // First delete the existing package while retaining the data directory - if (!deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA, + if (!deletePackageLI(pkgName, true, PackageManager.DONT_DELETE_DATA, res.removedInfo)) { // If the existing package was'nt successfully deleted res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; @@ -3933,51 +5660,21 @@ class PackageManagerService extends IPackageManager.Stub { } else { // Successfully deleted the old package. Now proceed with re-installation mLastScanError = PackageManager.INSTALL_SUCCEEDED; - newPackage = scanPackageLI(tmpPackageFile, destPackageFile, - destResourceFile, pkg, parseFlags, - SCAN_MONITOR | SCAN_FORCE_DEX - | SCAN_UPDATE_SIGNATURE - | (forwardLocked ? SCAN_FORWARD_LOCKED : 0) - | (newInstall ? SCAN_NEW_INSTALL : 0)); + newPackage = scanPackageLI(pkg, parseFlags, scanMode); if (newPackage == null) { - Log.w(TAG, "Package couldn't be installed in " + destPackageFile); + Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; } } else { - updateSettingsLI(pkgName, tmpPackageFile, - destFilePath, destPackageFile, - destResourceFile, pkg, - newPackage, - true, - forwardLocked, + updateSettingsLI(newPackage, installerPackageName, res); updatedSettings = true; } } - if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { - // If we deleted an exisiting package, the old source and resource files that we - // were keeping around in case we needed them (see below) can now be deleted - final ApplicationInfo deletedPackageAppInfo = deletedPackage.applicationInfo; - final ApplicationInfo installedPackageAppInfo = - newPackage.applicationInfo; - deletePackageResourcesLI(pkgName, - !deletedPackageAppInfo.sourceDir - .equals(installedPackageAppInfo.sourceDir) - ? deletedPackageAppInfo.sourceDir : null, - !deletedPackageAppInfo.publicSourceDir - .equals(installedPackageAppInfo.publicSourceDir) - ? deletedPackageAppInfo.publicSourceDir : null); - //update signature on the new package setting - //this should always succeed, since we checked the - //signature earlier. - synchronized(mPackages) { - verifySignaturesLP(mSettings.mPackages.get(pkgName), pkg, - parseFlags, true); - } - } else { + if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) { // remove package from internal structures. Note that we want deletePackageX to // delete the package data and cache directories that it created in // scanPackageLocked, unless those directories existed before we even tried to @@ -3993,55 +5690,52 @@ class PackageManagerService extends IPackageManager.Stub { if(deletedPkg) { File restoreFile = new File(deletedPackage.mPath); if (restoreFile == null) { - Log.e(TAG, "Failed allocating storage when restoring pkg : " + pkgName); - return; - } - File restoreTmpFile = createTempPackageFile(); - if (restoreTmpFile == null) { - Log.e(TAG, "Failed creating temp file when restoring pkg : " + pkgName); + Slog.e(TAG, "Failed allocating storage when restoring pkg : " + pkgName); return; } - if (!FileUtils.copyFile(restoreFile, restoreTmpFile)) { - Log.e(TAG, "Failed copying temp file when restoring pkg : " + pkgName); + // Parse old package + boolean oldOnSd = isExternal(deletedPackage); + int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | + (isForwardLocked(deletedPackage) ? PackageParser.PARSE_FORWARD_LOCK : 0) | + (oldOnSd ? PackageParser.PARSE_ON_SDCARD : 0); + int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE; + if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode) == null) { + Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade"); return; } - PackageInstalledInfo restoreRes = new PackageInstalledInfo(); - restoreRes.removedInfo = new PackageRemovedInfo(); - installPackageLI( - Uri.fromFile(restoreFile), - isForwardLocked(deletedPackage) - ? PackageManager.INSTALL_FORWARD_LOCK - : 0, false, oldInstallerPackageName, restoreTmpFile, restoreRes); - if (restoreRes.returnCode != PackageManager.INSTALL_SUCCEEDED) { - Log.e(TAG, "Failed restoring pkg : " + pkgName + " after failed upgrade"); + // Restore of old package succeeded. Update permissions. + synchronized (mPackages) { + updatePermissionsLP(deletedPackage.packageName, deletedPackage, + true, false, false); + mSettings.writeLP(); } + Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade"); } } } - + private void replaceSystemPackageLI(PackageParser.Package deletedPackage, - File tmpPackageFile, - String destFilePath, File destPackageFile, File destResourceFile, - PackageParser.Package pkg, boolean forwardLocked, boolean newInstall, + PackageParser.Package pkg, + int parseFlags, int scanMode, String installerPackageName, PackageInstalledInfo res) { PackageParser.Package newPackage = null; boolean updatedSettings = false; - int parseFlags = PackageManager.INSTALL_REPLACE_EXISTING | + parseFlags |= PackageManager.INSTALL_REPLACE_EXISTING | PackageParser.PARSE_IS_SYSTEM; String packageName = deletedPackage.packageName; res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; if (packageName == null) { - Log.w(TAG, "Attempt to delete null packageName."); + Slog.w(TAG, "Attempt to delete null packageName."); return; } PackageParser.Package oldPkg; PackageSetting oldPkgSetting; synchronized (mPackages) { oldPkg = mPackages.get(packageName); - oldPkgSetting = mSettings.mPackages.get(packageName); + oldPkgSetting = mSettings.mPackages.get(packageName); if((oldPkg == null) || (oldPkg.applicationInfo == null) || (oldPkgSetting == null)) { - Log.w(TAG, "Could'nt find package:"+packageName+" information"); + Slog.w(TAG, "Couldn't find package:"+packageName+" information"); return; } } @@ -4056,47 +5750,25 @@ class PackageManagerService extends IPackageManager.Stub { // Successfully disabled the old package. Now proceed with re-installation mLastScanError = PackageManager.INSTALL_SUCCEEDED; pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - newPackage = scanPackageLI(tmpPackageFile, destPackageFile, - destResourceFile, pkg, parseFlags, - SCAN_MONITOR | SCAN_FORCE_DEX - | SCAN_UPDATE_SIGNATURE - | (forwardLocked ? SCAN_FORWARD_LOCKED : 0) - | (newInstall ? SCAN_NEW_INSTALL : 0)); + newPackage = scanPackageLI(pkg, parseFlags, scanMode); if (newPackage == null) { - Log.w(TAG, "Package couldn't be installed in " + destPackageFile); + Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; } } else { - updateSettingsLI(packageName, tmpPackageFile, - destFilePath, destPackageFile, - destResourceFile, pkg, - newPackage, - true, - forwardLocked, - installerPackageName, - res); + updateSettingsLI(newPackage, installerPackageName, res); updatedSettings = true; } - if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { - //update signature on the new package setting - //this should always succeed, since we checked the - //signature earlier. - synchronized(mPackages) { - verifySignaturesLP(mSettings.mPackages.get(packageName), pkg, - parseFlags, true); - } - } else { + if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) { // Re installation failed. Restore old information // Remove new pkg information if (newPackage != null) { removePackageLI(newPackage, true); } // Add back the old system package - scanPackageLI(oldPkgSetting.codePath, oldPkgSetting.codePath, - oldPkgSetting.resourcePath, - oldPkg, parseFlags, + scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE); // Restore the old system information in Settings @@ -4110,15 +5782,23 @@ class PackageManagerService extends IPackageManager.Stub { } } } - - private void updateSettingsLI(String pkgName, File tmpPackageFile, - String destFilePath, File destPackageFile, - File destResourceFile, - PackageParser.Package pkg, - PackageParser.Package newPackage, - boolean replacingExistingPackage, - boolean forwardLocked, + + // Utility method used to move dex files during install. + private int moveDexFilesLI(PackageParser.Package newPackage) { + int retCode; + if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { + retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath); + if (retCode != 0) { + Slog.e(TAG, "Couldn't rename dex file: " + newPackage.mPath); + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } + } + return PackageManager.INSTALL_SUCCEEDED; + } + + private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName, PackageInstalledInfo res) { + String pkgName = newPackage.packageName; synchronized (mPackages) { //write settings. the installStatus will be incomplete at this stage. //note that the new package setting would have already been @@ -4127,40 +5807,23 @@ class PackageManagerService extends IPackageManager.Stub { mSettings.writeLP(); } - int retCode = 0; - if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - retCode = mInstaller.movedex(tmpPackageFile.toString(), - destPackageFile.toString()); - if (retCode != 0) { - Log.e(TAG, "Couldn't rename dex file: " + destPackageFile); - res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - return; - } - } - // XXX There are probably some big issues here: upon doing - // the rename, we have reached the point of no return (the - // original .apk is gone!), so we can't fail. Yet... we can. - if (!tmpPackageFile.renameTo(destPackageFile)) { - Log.e(TAG, "Couldn't move package file to: " + destPackageFile); - res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - } else { - res.returnCode = setPermissionsLI(pkgName, newPackage, destFilePath, - destResourceFile, - forwardLocked); - if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) { - return; - } else { - Log.d(TAG, "New package installed in " + destPackageFile); - } + if ((res.returnCode = moveDexFilesLI(newPackage)) + != PackageManager.INSTALL_SUCCEEDED) { + // Discontinue if moving dex files failed. + return; } - if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) { + if((res.returnCode = setPermissionsLI(newPackage)) + != PackageManager.INSTALL_SUCCEEDED) { if (mInstaller != null) { - mInstaller.rmdex(tmpPackageFile.getPath()); + mInstaller.rmdex(newPackage.mScanPath); } + return; + } else { + Log.d(TAG, "New package installed in " + newPackage.mPath); } - synchronized (mPackages) { - grantPermissionsLP(newPackage, true); + updatePermissionsLP(newPackage.packageName, newPackage, + newPackage.permissions.size() > 0, true, false); res.name = pkgName; res.uid = newPackage.applicationInfo.uid; res.pkg = newPackage; @@ -4171,193 +5834,147 @@ class PackageManagerService extends IPackageManager.Stub { mSettings.writeLP(); } } - - private File getFwdLockedResource(String pkgName) { - final String publicZipFileName = pkgName + ".zip"; - return new File(mAppInstallDir, publicZipFileName); - } - private File copyTempInstallFile(Uri pPackageURI, - PackageInstalledInfo res) { - File tmpPackageFile = createTempPackageFile(); - int retCode = PackageManager.INSTALL_SUCCEEDED; - if (tmpPackageFile == null) { - res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - return null; - } + private void installPackageLI(InstallArgs args, + boolean newInstall, PackageInstalledInfo res) { + int pFlags = args.flags; + String installerPackageName = args.installerPackageName; + File tmpPackageFile = new File(args.getCodePath()); + boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0); + boolean onSd = ((pFlags & PackageManager.INSTALL_EXTERNAL) != 0); + boolean replace = false; + int scanMode = (onSd ? 0 : SCAN_MONITOR) | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE + | (newInstall ? SCAN_NEW_INSTALL : 0); + // Result object to be returned + res.returnCode = PackageManager.INSTALL_SUCCEEDED; - if (pPackageURI.getScheme().equals("file")) { - final File srcPackageFile = new File(pPackageURI.getPath()); - // We copy the source package file to a temp file and then rename it to the - // destination file in order to eliminate a window where the package directory - // scanner notices the new package file but it's not completely copied yet. - if (!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) { - Log.e(TAG, "Couldn't copy package file to temp file."); - retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - } - } else if (pPackageURI.getScheme().equals("content")) { - ParcelFileDescriptor fd = null; - try { - fd = mContext.getContentResolver().openFileDescriptor(pPackageURI, "r"); - } catch (FileNotFoundException e) { - Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e); - retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - } - if (fd == null) { - Log.e(TAG, "Couldn't open file descriptor from download service (null)."); - retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - } else { - if (Config.LOGV) { - Log.v(TAG, "Opened file descriptor from download service."); - } - ParcelFileDescriptor.AutoCloseInputStream - dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd); - // We copy the source package file to a temp file and then rename it to the - // destination file in order to eliminate a window where the package directory - // scanner notices the new package file but it's not completely copied yet. - if (!FileUtils.copyToFile(dlStream, tmpPackageFile)) { - Log.e(TAG, "Couldn't copy package stream to temp file."); - retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - } - } - } else { - Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); - retCode = PackageManager.INSTALL_FAILED_INVALID_URI; + // Retrieve PackageSettings and parse package + int parseFlags = PackageParser.PARSE_CHATTY | + (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) | + (onSd ? PackageParser.PARSE_ON_SDCARD : 0); + parseFlags |= mDefParseFlags; + PackageParser pp = new PackageParser(tmpPackageFile.getPath()); + pp.setSeparateProcesses(mSeparateProcesses); + final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile, + null, mMetrics, parseFlags); + if (pkg == null) { + res.returnCode = pp.getParseError(); + return; } - - res.returnCode = retCode; - if (retCode != PackageManager.INSTALL_SUCCEEDED) { - if (tmpPackageFile != null && tmpPackageFile.exists()) { - tmpPackageFile.delete(); + String pkgName = res.name = pkg.packageName; + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) { + if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) { + res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY; + return; } - return null; } - return tmpPackageFile; - } - - private void installPackageLI(Uri pPackageURI, - int pFlags, boolean newInstall, String installerPackageName, - File tmpPackageFile, PackageInstalledInfo res) { - String pkgName = null; - boolean forwardLocked = false; - boolean replacingExistingPackage = false; - // Result object to be returned - res.returnCode = PackageManager.INSTALL_SUCCEEDED; + if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) { + res.returnCode = pp.getParseError(); + return; + } + // Get rid of all references to package scan path via parser. + pp = null; + String oldCodePath = null; + boolean systemApp = false; + synchronized (mPackages) { + // Check if installing already existing package + if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0) { + String oldName = mSettings.mRenamedPackages.get(pkgName); + if (pkg.mOriginalPackages != null + && pkg.mOriginalPackages.contains(oldName) + && mPackages.containsKey(oldName)) { + // This package is derived from an original package, + // and this device has been updating from that original + // name. We must continue using the original name, so + // rename the new package here. + pkg.setPackageName(oldName); + pkgName = pkg.packageName; + replace = true; + } else if (mPackages.containsKey(pkgName)) { + // This package, under its official name, already exists + // on the device; we should replace it. + replace = true; + } + } + PackageSetting ps = mSettings.mPackages.get(pkgName); + if (ps != null) { + oldCodePath = mSettings.mPackages.get(pkgName).codePathString; + if (ps.pkg != null && ps.pkg.applicationInfo != null) { + systemApp = (ps.pkg.applicationInfo.flags & + ApplicationInfo.FLAG_SYSTEM) != 0; + } + } + } + + if (systemApp && onSd) { + // Disable updates to system apps on sdcard + Slog.w(TAG, "Cannot install updates to system apps on sdcard"); + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; + return; + } - main_flow: try { - pkgName = PackageParser.parsePackageName( - tmpPackageFile.getAbsolutePath(), 0); - if (pkgName == null) { - Log.e(TAG, "Couldn't find a package name in : " + tmpPackageFile); - res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; - break main_flow; - } - res.name = pkgName; - //initialize some variables before installing pkg - final String pkgFileName = pkgName + ".apk"; - final File destDir = ((pFlags&PackageManager.INSTALL_FORWARD_LOCK) != 0) - ? mDrmAppPrivateInstallDir - : mAppInstallDir; - final File destPackageFile = new File(destDir, pkgFileName); - final String destFilePath = destPackageFile.getAbsolutePath(); - File destResourceFile; - if ((pFlags&PackageManager.INSTALL_FORWARD_LOCK) != 0) { - destResourceFile = getFwdLockedResource(pkgName); - forwardLocked = true; - } else { - destResourceFile = destPackageFile; - } - // Retrieve PackageSettings and parse package - int parseFlags = PackageParser.PARSE_CHATTY; - parseFlags |= mDefParseFlags; - PackageParser pp = new PackageParser(tmpPackageFile.getPath()); - pp.setSeparateProcesses(mSeparateProcesses); - final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile, - destPackageFile.getAbsolutePath(), mMetrics, parseFlags); - if (pkg == null) { - res.returnCode = pp.getParseError(); - break main_flow; - } - if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) { - if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) { - res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY; - break main_flow; - } - } - if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) { - res.returnCode = pp.getParseError(); - break main_flow; - } - - synchronized (mPackages) { - //check if installing already existing package - if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0 - && mPackages.containsKey(pkgName)) { - replacingExistingPackage = true; - } - } - - if(replacingExistingPackage) { - replacePackageLI(pkgName, - tmpPackageFile, - destFilePath, destPackageFile, destResourceFile, - pkg, forwardLocked, newInstall, installerPackageName, - res); - } else { - installNewPackageLI(pkgName, - tmpPackageFile, - destFilePath, destPackageFile, destResourceFile, - pkg, forwardLocked, newInstall, installerPackageName, - res); - } - } finally { - if (tmpPackageFile != null && tmpPackageFile.exists()) { - tmpPackageFile.delete(); - } + if (!args.doRename(res.returnCode, pkgName, oldCodePath)) { + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return; + } + // Set application objects path explicitly after the rename + setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath()); + if (replace) { + replacePackageLI(pkg, parseFlags, scanMode, + installerPackageName, res); + } else { + installNewPackageLI(pkg, parseFlags, scanMode, + installerPackageName,res); } } - - private int setPermissionsLI(String pkgName, - PackageParser.Package newPackage, - String destFilePath, - File destResourceFile, - boolean forwardLocked) { - int retCode; - if (forwardLocked) { + + private int setPermissionsLI(PackageParser.Package newPackage) { + String pkgName = newPackage.packageName; + int retCode = 0; + // TODO Gross hack but fix later. Ideally move this to be a post installation + // check after alloting uid. + if ((newPackage.applicationInfo.flags + & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) { + File destResourceFile = new File(newPackage.applicationInfo.publicSourceDir); try { extractPublicFiles(newPackage, destResourceFile); } catch (IOException e) { - Log.e(TAG, "Couldn't create a new zip file for the public parts of a" + + Slog.e(TAG, "Couldn't create a new zip file for the public parts of a" + " forward-locked app."); return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } finally { //TODO clean up the extracted public files } if (mInstaller != null) { - retCode = mInstaller.setForwardLockPerm(pkgName, + retCode = mInstaller.setForwardLockPerm(getApkName(newPackage.mPath), newPackage.applicationInfo.uid); } else { final int filePermissions = FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP; - retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1, + retCode = FileUtils.setPermissions(newPackage.mPath, filePermissions, -1, newPackage.applicationInfo.uid); } } else { - final int filePermissions = - FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP - |FileUtils.S_IROTH; - retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1, -1); + // The permissions on the resource file was set when it was copied for + // non forward locked apps and apps on sdcard } + if (retCode != 0) { - Log.e(TAG, "Couldn't set new package file permissions for " + destFilePath + Slog.e(TAG, "Couldn't set new package file permissions for " + + newPackage.mPath + ". The return code was: " + retCode); + // TODO Define new internal error + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } return PackageManager.INSTALL_SUCCEEDED; } - private boolean isForwardLocked(PackageParser.Package deletedPackage) { - final ApplicationInfo applicationInfo = deletedPackage.applicationInfo; - return applicationInfo.sourceDir.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath()); + private boolean isForwardLocked(PackageParser.Package pkg) { + return ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); + } + + private boolean isExternal(PackageParser.Package pkg) { + return ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); } private void extractPublicFiles(PackageParser.Package newPackage, @@ -4417,7 +6034,7 @@ class PackageManagerService extends IPackageManager.Stub { } outZipStream.flush(); } - + private void deleteTempPackageFiles() { FilenameFilter filter = new FilenameFilter() { public boolean accept(File dir, String name) { @@ -4434,12 +6051,12 @@ class PackageManagerService extends IPackageManager.Stub { } } - private File createTempPackageFile() { + private File createTempPackageFile(File installDir) { File tmpPackageFile; try { - tmpPackageFile = File.createTempFile("vmdl", ".tmp", mAppInstallDir); + tmpPackageFile = File.createTempFile("vmdl", ".tmp", installDir); } catch (IOException e) { - Log.e(TAG, "Couldn't create temp file for downloaded package file."); + Slog.e(TAG, "Couldn't create temp file for downloaded package file."); return null; } try { @@ -4447,7 +6064,7 @@ class PackageManagerService extends IPackageManager.Stub { tmpPackageFile.getCanonicalPath(), FileUtils.S_IRUSR|FileUtils.S_IWUSR, -1, -1); } catch (IOException e) { - Log.e(TAG, "Trouble getting the canoncical path for a temp file."); + Slog.e(TAG, "Trouble getting the canoncical path for a temp file."); return null; } return tmpPackageFile; @@ -4473,13 +6090,13 @@ class PackageManagerService extends IPackageManager.Stub { } //end run }); } - + /** * This method is an internal method that could be get invoked either * to delete an installed package or to clean up a failed installation. * After deleting an installed package, a broadcast is sent to notify any * listeners that the package has been installed. For cleaning up a failed - * installation, the broadcast is not necessary since the package's + * installation, the broadcast is not necessary since the package's * installation wouldn't have sent the initial broadcast either * The key steps in deleting a package are * deleting the package information in internal structures like mPackages, @@ -4488,16 +6105,26 @@ class PackageManagerService extends IPackageManager.Stub { * persisting settings for later use * sending a broadcast if necessary */ - private boolean deletePackageX(String packageName, boolean sendBroadCast, boolean deleteCodeAndResources, int flags) { PackageRemovedInfo info = new PackageRemovedInfo(); boolean res; - synchronized (mInstallLock) { - res = deletePackageLI(packageName, deleteCodeAndResources, flags, info); + IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface( + ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)); + try { + if (dpm != null && dpm.packageHasActiveAdmins(packageName)) { + Slog.w(TAG, "Not removing package " + packageName + ": has active device admin"); + return false; + } + } catch (RemoteException e) { } + synchronized (mInstallLock) { + res = deletePackageLI(packageName, deleteCodeAndResources, + flags | REMOVE_CHATTY, info); + } + if(res && sendBroadCast) { boolean systemUpdate = info.isRemovedPackageSystemUpdate; info.sendBroadcast(deleteCodeAndResources, systemUpdate); @@ -4509,8 +6136,17 @@ class PackageManagerService extends IPackageManager.Stub { extras.putInt(Intent.EXTRA_UID, info.removedUid >= 0 ? info.removedUid : info.uid); extras.putBoolean(Intent.EXTRA_REPLACING, true); - sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras); - sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras); + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, null); + sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, null); + } + } + // Force a gc here. + Runtime.getRuntime().gc(); + // Delete the resources here after sending the broadcast to let + // other processes clean up before deleting resources. + if (info.args != null) { + synchronized (mInstallLock) { + info.args.doPostDeleteLI(deleteCodeAndResources); } } return res; @@ -4521,6 +6157,8 @@ class PackageManagerService extends IPackageManager.Stub { int uid = -1; int removedUid = -1; boolean isRemovedPackageSystemUpdate = false; + // Clean up resources deleted packages. + InstallArgs args = null; void sendBroadcast(boolean fullRemove, boolean replacing) { Bundle extras = new Bundle(1); @@ -4530,27 +6168,27 @@ class PackageManagerService extends IPackageManager.Stub { extras.putBoolean(Intent.EXTRA_REPLACING, true); } if (removedPackage != null) { - sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras); + sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras, null); } if (removedUid >= 0) { - sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras); + sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null); } } } - + /* * This method deletes the package from internal data structures. If the DONT_DELETE_DATA * flag is not set, the data directory is removed as well. - * make sure this flag is set for partially installed apps. If not its meaningless to + * make sure this flag is set for partially installed apps. If not its meaningless to * delete a partially installed application. */ - private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo, + private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo, int flags) { String packageName = p.packageName; if (outInfo != null) { outInfo.removedPackage = packageName; } - removePackageLI(p, true); + removePackageLI(p, (flags&REMOVE_CHATTY) != 0); // Retrieve object to delete permissions for shared user later on PackageSetting deletedPs; synchronized (mPackages) { @@ -4560,7 +6198,7 @@ class PackageManagerService extends IPackageManager.Stub { if (mInstaller != null) { int retCode = mInstaller.remove(packageName); if (retCode < 0) { - Log.w(TAG, "Couldn't remove app data or cache directory for package: " + Slog.w(TAG, "Couldn't remove app data or cache directory for package: " + packageName + ", retcode=" + retCode); // we don't consider this to be a failure of the core package deletion } @@ -4570,22 +6208,39 @@ class PackageManagerService extends IPackageManager.Stub { File dataDir = new File(pkg.applicationInfo.dataDir); dataDir.delete(); } - synchronized (mPackages) { - if (outInfo != null) { - outInfo.removedUid = mSettings.removePackageLP(packageName); - } - } } synchronized (mPackages) { - if ( (deletedPs != null) && (deletedPs.sharedUser != null)) { - // remove permissions associated with package - mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids); + if (deletedPs != null) { + schedulePackageCleaning(packageName); + + if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { + if (outInfo != null) { + outInfo.removedUid = mSettings.removePackageLP(packageName); + } + if (deletedPs != null) { + updatePermissionsLP(deletedPs.name, null, false, false, false); + if (deletedPs.sharedUser != null) { + // remove permissions associated with package + mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids); + } + } + } + // remove from preferred activities. + ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>(); + for (PreferredActivity pa : mSettings.mPreferredActivities.filterSet()) { + if (pa.mActivity.getPackageName().equals(deletedPs.name)) { + removed.add(pa); + } + } + for (PreferredActivity pa : removed) { + mSettings.mPreferredActivities.removeFilter(pa); + } } // Save settings now - mSettings.writeLP (); + mSettings.writeLP(); } } - + /* * Tries to delete system package. */ @@ -4594,7 +6249,7 @@ class PackageManagerService extends IPackageManager.Stub { ApplicationInfo applicationInfo = p.applicationInfo; //applicable for non-partially installed applications only if (applicationInfo == null) { - Log.w(TAG, "Package " + p.packageName + " has no applicationInfo."); + Slog.w(TAG, "Package " + p.packageName + " has no applicationInfo."); return false; } PackageSetting ps = null; @@ -4605,7 +6260,7 @@ class PackageManagerService extends IPackageManager.Stub { ps = mSettings.getDisabledSystemPkg(p.packageName); } if (ps == null) { - Log.w(TAG, "Attempt to delete system package "+ p.packageName); + Slog.w(TAG, "Attempt to delete system package "+ p.packageName); return false; } else { Log.i(TAG, "Deleting system pkg from data partition"); @@ -4632,58 +6287,30 @@ class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { // Reinstate the old system package mSettings.enableSystemPackageLP(p.packageName); + // Remove any native libraries. + removeNativeBinariesLI(p); } // Install the system package - PackageParser.Package newPkg = scanPackageLI(ps.codePath, ps.codePath, ps.resourcePath, + PackageParser.Package newPkg = scanPackageLI(ps.codePath, PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM, - SCAN_MONITOR); - + SCAN_MONITOR | SCAN_NO_PATHS); + if (newPkg == null) { - Log.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError); + Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError); return false; } synchronized (mPackages) { - grantPermissionsLP(newPkg, true); + updatePermissionsLP(newPkg.packageName, newPkg, true, true, false); mSettings.writeLP(); } return true; } - private void deletePackageResourcesLI(String packageName, - String sourceDir, String publicSourceDir) { - if (sourceDir != null) { - File sourceFile = new File(sourceDir); - if (!sourceFile.exists()) { - Log.w(TAG, "Package source " + sourceDir + " does not exist."); - } - // Delete application's code and resources - sourceFile.delete(); - if (mInstaller != null) { - int retCode = mInstaller.rmdex(sourceFile.toString()); - if (retCode < 0) { - Log.w(TAG, "Couldn't remove dex file for package: " - + packageName + " at location " - + sourceFile.toString() + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion - } - } - } - if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) { - final File publicSourceFile = new File(publicSourceDir); - if (!publicSourceFile.exists()) { - Log.w(TAG, "Package public source " + publicSourceFile + " does not exist."); - } - if (publicSourceFile.exists()) { - publicSourceFile.delete(); - } - } - } - private boolean deleteInstalledPackageLI(PackageParser.Package p, boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) { ApplicationInfo applicationInfo = p.applicationInfo; if (applicationInfo == null) { - Log.w(TAG, "Package " + p.packageName + " has no applicationInfo."); + Slog.w(TAG, "Package " + p.packageName + " has no applicationInfo."); return false; } if (outInfo != null) { @@ -4695,19 +6322,24 @@ class PackageManagerService extends IPackageManager.Stub { // Delete application code and resources if (deleteCodeAndResources) { - deletePackageResourcesLI(applicationInfo.packageName, + // TODO can pick up from PackageSettings as well + int installFlags = ((p.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE)!=0) ? + PackageManager.INSTALL_EXTERNAL : 0; + installFlags |= ((p.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK)!=0) ? + PackageManager.INSTALL_FORWARD_LOCK : 0; + outInfo.args = createInstallArgs(installFlags, applicationInfo.sourceDir, applicationInfo.publicSourceDir); } return true; } - + /* * This method handles package deletion in general */ private boolean deletePackageLI(String packageName, boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) { if (packageName == null) { - Log.w(TAG, "Attempt to delete null packageName."); + Slog.w(TAG, "Attempt to delete null packageName."); return false; } PackageParser.Package p; @@ -4719,17 +6351,17 @@ class PackageManagerService extends IPackageManager.Stub { dataOnly = true; PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { - Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); return false; } p = ps.pkg; } } if (p == null) { - Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); return false; } - + if (dataOnly) { // Delete application data first removePackageDataLI(p, outInfo, flags); @@ -4737,19 +6369,24 @@ class PackageManagerService extends IPackageManager.Stub { } // At this point the package should have ApplicationInfo associated with it if (p.applicationInfo == null) { - Log.w(TAG, "Package " + p.packageName + " has no applicationInfo."); + Slog.w(TAG, "Package " + p.packageName + " has no applicationInfo."); return false; } + boolean ret = false; if ( (p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { Log.i(TAG, "Removing system package:"+p.packageName); // When an updated system application is deleted we delete the existing resources as well and // fall back to existing code in system partition - return deleteSystemPackageLI(p, flags, outInfo); + ret = deleteSystemPackageLI(p, flags, outInfo); + } else { + Log.i(TAG, "Removing non-system package:"+p.packageName); + // Kill application pre-emptively especially for apps on sd. + killApplication(packageName, p.applicationInfo.uid); + ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo); } - Log.i(TAG, "Removing non-system package:"+p.packageName); - return deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo); + return ret; } - + public void clearApplicationUserData(final String packageName, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( @@ -4780,10 +6417,10 @@ class PackageManagerService extends IPackageManager.Stub { } //end run }); } - + private boolean clearApplicationUserDataLI(String packageName) { if (packageName == null) { - Log.w(TAG, "Attempt to delete null packageName."); + Slog.w(TAG, "Attempt to delete null packageName."); return false; } PackageParser.Package p; @@ -4794,28 +6431,29 @@ class PackageManagerService extends IPackageManager.Stub { dataOnly = true; PackageSetting ps = mSettings.mPackages.get(packageName); if((ps == null) || (ps.pkg == null)) { - Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); return false; } p = ps.pkg; } } + if(!dataOnly) { //need to check this only for fully installed applications if (p == null) { - Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); return false; } final ApplicationInfo applicationInfo = p.applicationInfo; if (applicationInfo == null) { - Log.w(TAG, "Package " + packageName + " has no applicationInfo."); + Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } } if (mInstaller != null) { int retCode = mInstaller.clearUserData(packageName); if (retCode < 0) { - Log.w(TAG, "Couldn't remove cache files for package: " + Slog.w(TAG, "Couldn't remove cache files for package: " + packageName); return false; } @@ -4848,7 +6486,7 @@ class PackageManagerService extends IPackageManager.Stub { private boolean deleteApplicationCacheFilesLI(String packageName) { if (packageName == null) { - Log.w(TAG, "Attempt to delete null packageName."); + Slog.w(TAG, "Attempt to delete null packageName."); return false; } PackageParser.Package p; @@ -4856,18 +6494,18 @@ class PackageManagerService extends IPackageManager.Stub { p = mPackages.get(packageName); } if (p == null) { - Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); return false; } final ApplicationInfo applicationInfo = p.applicationInfo; if (applicationInfo == null) { - Log.w(TAG, "Package " + packageName + " has no applicationInfo."); + Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } if (mInstaller != null) { int retCode = mInstaller.deleteCacheFiles(packageName); if (retCode < 0) { - Log.w(TAG, "Couldn't remove cache files for package: " + Slog.w(TAG, "Couldn't remove cache files for package: " + packageName); return false; } @@ -4901,7 +6539,7 @@ class PackageManagerService extends IPackageManager.Stub { private boolean getPackageSizeInfoLI(String packageName, PackageStats pStats) { if (packageName == null) { - Log.w(TAG, "Attempt to get size of null packageName."); + Slog.w(TAG, "Attempt to get size of null packageName."); return false; } PackageParser.Package p; @@ -4912,7 +6550,7 @@ class PackageManagerService extends IPackageManager.Stub { dataOnly = true; PackageSetting ps = mSettings.mPackages.get(packageName); if((ps == null) || (ps.pkg == null)) { - Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); return false; } p = ps.pkg; @@ -4922,7 +6560,7 @@ class PackageManagerService extends IPackageManager.Stub { if(!dataOnly) { final ApplicationInfo applicationInfo = p.applicationInfo; if (applicationInfo == null) { - Log.w(TAG, "Package " + packageName + " has no applicationInfo."); + Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null; @@ -4939,41 +6577,74 @@ class PackageManagerService extends IPackageManager.Stub { return true; } - + public void addPackageToPreferred(String packageName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); - Log.w(TAG, "addPackageToPreferred: no longer implemented"); + Slog.w(TAG, "addPackageToPreferred: no longer implemented"); } public void removePackageFromPreferred(String packageName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); - Log.w(TAG, "removePackageFromPreferred: no longer implemented"); + Slog.w(TAG, "removePackageFromPreferred: no longer implemented"); } public List<PackageInfo> getPreferredPackages(int flags) { return new ArrayList<PackageInfo>(); } + int getUidTargetSdkVersionLockedLP(int uid) { + Object obj = mSettings.getUserIdLP(uid); + if (obj instanceof SharedUserSetting) { + SharedUserSetting sus = (SharedUserSetting)obj; + final int N = sus.packages.size(); + int vers = Build.VERSION_CODES.CUR_DEVELOPMENT; + Iterator<PackageSetting> it = sus.packages.iterator(); + int i=0; + while (it.hasNext()) { + PackageSetting ps = it.next(); + if (ps.pkg != null) { + int v = ps.pkg.applicationInfo.targetSdkVersion; + if (v < vers) vers = v; + } + } + return vers; + } else if (obj instanceof PackageSetting) { + PackageSetting ps = (PackageSetting)obj; + if (ps.pkg != null) { + return ps.pkg.applicationInfo.targetSdkVersion; + } + } + return Build.VERSION_CODES.CUR_DEVELOPMENT; + } + public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); - synchronized (mPackages) { - Log.i(TAG, "Adding preferred activity " + activity + ":"); + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS) + != PackageManager.PERMISSION_GRANTED) { + if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid()) + < Build.VERSION_CODES.FROYO) { + Slog.w(TAG, "Ignoring addPreferredActivity() from uid " + + Binder.getCallingUid()); + return; + } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + } + + Slog.i(TAG, "Adding preferred activity " + activity + ":"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); mSettings.mPreferredActivities.addFilter( new PreferredActivity(filter, match, set, activity)); - mSettings.writeLP(); + scheduleWriteSettingsLocked(); } } public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); if (filter.countActions() != 1) { throw new IllegalArgumentException( "replacePreferredActivity expects filter to have only 1 action."); @@ -4991,6 +6662,19 @@ class PackageManagerService extends IPackageManager.Stub { "paths, schemes or types."); } synchronized (mPackages) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS) + != PackageManager.PERMISSION_GRANTED) { + if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid()) + < Build.VERSION_CODES.FROYO) { + Slog.w(TAG, "Ignoring replacePreferredActivity() from uid " + + Binder.getCallingUid()); + return; + } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + } + Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); String action = filter.getAction(0); String category = filter.getCategory(0); @@ -5007,12 +6691,26 @@ class PackageManagerService extends IPackageManager.Stub { } public void clearPackagePreferredActivities(String packageName) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); - synchronized (mPackages) { + int uid = Binder.getCallingUid(); + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null || pkg.applicationInfo.uid != uid) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS) + != PackageManager.PERMISSION_GRANTED) { + if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid()) + < Build.VERSION_CODES.FROYO) { + Slog.w(TAG, "Ignoring clearPackagePreferredActivities() from uid " + + Binder.getCallingUid()); + return; + } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + } + } + if (clearPackagePreferredActivitiesLP(packageName)) { - mSettings.writeLP(); + scheduleWriteSettingsLocked(); } } } @@ -5101,21 +6799,31 @@ class PackageManagerService extends IPackageManager.Stub { } if (className == null) { // We're dealing with an application/package level state change + if (pkgSetting.enabled == newState) { + // Nothing to do + return; + } pkgSetting.enabled = newState; } else { // We're dealing with a component level state change switch (newState) { case COMPONENT_ENABLED_STATE_ENABLED: - pkgSetting.enableComponentLP(className); + if (!pkgSetting.enableComponentLP(className)) { + return; + } break; case COMPONENT_ENABLED_STATE_DISABLED: - pkgSetting.disableComponentLP(className); + if (!pkgSetting.disableComponentLP(className)) { + return; + } break; case COMPONENT_ENABLED_STATE_DEFAULT: - pkgSetting.restoreComponentLP(className); + if (!pkgSetting.restoreComponentLP(className)) { + return; + } break; default: - Log.e(TAG, "Invalid new component state: " + newState); + Slog.e(TAG, "Invalid new component state: " + newState); return; } } @@ -5167,7 +6875,7 @@ class PackageManagerService extends IPackageManager.Stub { extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList); extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag); extras.putInt(Intent.EXTRA_UID, packageUid); - sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras); + sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null); } public String getInstallerPackageName(String packageName) { @@ -5179,7 +6887,7 @@ class PackageManagerService extends IPackageManager.Stub { return pkg.installerPackageName; } } - + public int getApplicationEnabledSetting(String appPackageName) { synchronized (mPackages) { PackageSetting pkg = mSettings.mPackages.get(appPackageName); @@ -5241,7 +6949,7 @@ class PackageManagerService extends IPackageManager.Stub { buf.append(']'); return buf.toString(); } - + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -5254,38 +6962,110 @@ class PackageManagerService extends IPackageManager.Stub { return; } + String packageName = null; + + int opti = 0; + while (opti < args.length) { + String opt = args[opti]; + if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') { + break; + } + opti++; + if ("-a".equals(opt)) { + // Right now we only know how to print all. + } else if ("-h".equals(opt)) { + pw.println("Package manager dump options:"); + pw.println(" [-h] [cmd] ..."); + pw.println(" cmd may be one of:"); + pw.println(" [package.name]: info about given package"); + return; + } else { + pw.println("Unknown argument: " + opt + "; use -h for help"); + } + } + + // Is the caller requesting to dump a particular piece of data? + if (opti < args.length) { + String cmd = args[opti]; + opti++; + // Is this a package name? + if ("android".equals(cmd) || cmd.contains(".")) { + packageName = cmd; + } + } + + boolean printedTitle = false; + synchronized (mPackages) { - pw.println("Activity Resolver Table:"); - mActivities.dump(pw, " "); - pw.println(" "); - pw.println("Receiver Resolver Table:"); - mReceivers.dump(pw, " "); - pw.println(" "); - pw.println("Service Resolver Table:"); - mServices.dump(pw, " "); - pw.println(" "); - pw.println("Preferred Activities:"); - mSettings.mPreferredActivities.dump(pw, " "); - pw.println(" "); - pw.println("Permissions:"); + if (mActivities.dump(pw, "Activity Resolver Table:", " ", packageName)) { + printedTitle = true; + } + if (mReceivers.dump(pw, printedTitle + ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:", + " ", packageName)) { + printedTitle = true; + } + if (mServices.dump(pw, printedTitle + ? "\nService Resolver Table:" : "Service Resolver Table:", + " ", packageName)) { + printedTitle = true; + } + if (mSettings.mPreferredActivities.dump(pw, printedTitle + ? "\nPreferred Activities:" : "Preferred Activities:", + " ", packageName)) { + printedTitle = true; + } + boolean printedSomething = false; { for (BasePermission p : mSettings.mPermissions.values()) { + if (packageName != null && !packageName.equals(p.sourcePackage)) { + continue; + } + if (!printedSomething) { + if (printedTitle) pw.println(" "); + pw.println("Permissions:"); + printedSomething = true; + printedTitle = true; + } pw.print(" Permission ["); pw.print(p.name); pw.print("] ("); pw.print(Integer.toHexString(System.identityHashCode(p))); pw.println("):"); pw.print(" sourcePackage="); pw.println(p.sourcePackage); pw.print(" uid="); pw.print(p.uid); pw.print(" gids="); pw.print(arrayToString(p.gids)); - pw.print(" type="); pw.println(p.type); + pw.print(" type="); pw.print(p.type); + pw.print(" prot="); pw.println(p.protectionLevel); + if (p.packageSetting != null) { + pw.print(" packageSetting="); pw.println(p.packageSetting); + } + if (p.perm != null) { + pw.print(" perm="); pw.println(p.perm); + } } } - pw.println(" "); - pw.println("Packages:"); + printedSomething = false; + SharedUserSetting packageSharedUser = null; { for (PackageSetting ps : mSettings.mPackages.values()) { - pw.print(" Package ["); pw.print(ps.name); pw.print("] ("); + if (packageName != null && !packageName.equals(ps.realName) + && !packageName.equals(ps.name)) { + continue; + } + if (!printedSomething) { + if (printedTitle) pw.println(" "); + pw.println("Packages:"); + printedSomething = true; + printedTitle = true; + } + packageSharedUser = ps.sharedUser; + pw.print(" Package ["); + pw.print(ps.realName != null ? ps.realName : ps.name); + pw.print("] ("); pw.print(Integer.toHexString(System.identityHashCode(ps))); pw.println("):"); + if (ps.realName != null) { + pw.print(" compat name="); pw.println(ps.name); + } pw.print(" userId="); pw.print(ps.userId); pw.print(" gids="); pw.println(arrayToString(ps.gids)); pw.print(" sharedUser="); pw.println(ps.sharedUser); @@ -5297,31 +7077,31 @@ class PackageManagerService extends IPackageManager.Stub { pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion); pw.print(" supportsScreens=["); boolean first = true; - if ((ps.pkg.applicationInfo.flags & + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) { if (!first) pw.print(", "); first = false; pw.print("medium"); } - if ((ps.pkg.applicationInfo.flags & + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) { if (!first) pw.print(", "); first = false; pw.print("large"); } - if ((ps.pkg.applicationInfo.flags & + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) { if (!first) pw.print(", "); first = false; pw.print("small"); } - if ((ps.pkg.applicationInfo.flags & + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) { if (!first) pw.print(", "); first = false; pw.print("resizeable"); } - if ((ps.pkg.applicationInfo.flags & + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) { if (!first) pw.print(", "); first = false; @@ -5332,7 +7112,8 @@ class PackageManagerService extends IPackageManager.Stub { pw.print(" timeStamp="); pw.println(ps.getTimeStampStr()); pw.print(" signatures="); pw.println(ps.signatures); pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed); - pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags)); + pw.print(" haveGids="); pw.println(ps.haveGids); + pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags)); pw.print(" installStatus="); pw.print(ps.installStatus); pw.print(" enabled="); pw.println(ps.enabled); if (ps.disabledComponents.size() > 0) { @@ -5353,18 +7134,65 @@ class PackageManagerService extends IPackageManager.Stub { pw.print(" "); pw.println(s); } } - if (ps.loadedPermissions.size() > 0) { - pw.println(" loadedPermissions:"); - for (String s : ps.loadedPermissions) { - pw.print(" "); pw.println(s); - } + } + } + printedSomething = false; + if (mSettings.mRenamedPackages.size() > 0) { + for (HashMap.Entry<String, String> e + : mSettings.mRenamedPackages.entrySet()) { + if (packageName != null && !packageName.equals(e.getKey()) + && !packageName.equals(e.getValue())) { + continue; + } + if (!printedSomething) { + if (printedTitle) pw.println(" "); + pw.println("Renamed packages:"); + printedSomething = true; + printedTitle = true; } + pw.print(" "); pw.print(e.getKey()); pw.print(" -> "); + pw.println(e.getValue()); } } - pw.println(" "); - pw.println("Shared Users:"); + printedSomething = false; + if (mSettings.mDisabledSysPackages.size() > 0) { + for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) { + if (packageName != null && !packageName.equals(ps.realName) + && !packageName.equals(ps.name)) { + continue; + } + if (!printedSomething) { + if (printedTitle) pw.println(" "); + pw.println("Hidden system packages:"); + printedSomething = true; + printedTitle = true; + } + pw.print(" Package ["); + pw.print(ps.realName != null ? ps.realName : ps.name); + pw.print("] ("); + pw.print(Integer.toHexString(System.identityHashCode(ps))); + pw.println("):"); + if (ps.realName != null) { + pw.print(" compat name="); pw.println(ps.name); + } + pw.print(" userId="); pw.println(ps.userId); + pw.print(" sharedUser="); pw.println(ps.sharedUser); + pw.print(" codePath="); pw.println(ps.codePathString); + pw.print(" resourcePath="); pw.println(ps.resourcePathString); + } + } + printedSomething = false; { for (SharedUserSetting su : mSettings.mSharedUsers.values()) { + if (packageName != null && su != packageSharedUser) { + continue; + } + if (!printedSomething) { + if (printedTitle) pw.println(" "); + pw.println("Shared users:"); + printedSomething = true; + printedTitle = true; + } pw.print(" SharedUser ["); pw.print(su.name); pw.print("] ("); pw.print(Integer.toHexString(System.identityHashCode(su))); pw.println("):"); @@ -5374,22 +7202,44 @@ class PackageManagerService extends IPackageManager.Stub { for (String s : su.grantedPermissions) { pw.print(" "); pw.println(s); } - pw.println(" loadedPermissions:"); - for (String s : su.loadedPermissions) { - pw.print(" "); pw.println(s); - } } } - pw.println(" "); - pw.println("Settings parse messages:"); - pw.println(mSettings.mReadMessages.toString()); + + if (packageName == null) { + if (printedTitle) pw.println(" "); + printedTitle = true; + pw.println("Settings parse messages:"); + pw.println(mSettings.mReadMessages.toString()); + + pw.println(" "); + pw.println("Package warning messages:"); + File fname = getSettingsProblemFile(); + FileInputStream in; + try { + in = new FileInputStream(fname); + int avail = in.available(); + byte[] data = new byte[avail]; + in.read(data); + pw.println(new String(data)); + } catch (FileNotFoundException e) { + } catch (IOException e) { + } + } } synchronized (mProviders) { - pw.println(" "); - pw.println("Registered ContentProviders:"); + boolean printedSomething = false; for (PackageParser.Provider p : mProviders.values()) { - pw.println(" ["); pw.println(p.info.authority); pw.println("]: "); + if (packageName != null && !packageName.equals(p.info.packageName)) { + continue; + } + if (!printedSomething) { + if (printedTitle) pw.println(" "); + pw.println("Registered ContentProviders:"); + printedSomething = true; + printedTitle = true; + } + pw.print(" ["); pw.print(p.info.authority); pw.print("]: "); pw.println(p.toString()); } } @@ -5401,8 +7251,10 @@ class PackageManagerService extends IPackageManager.Stub { final static int TYPE_DYNAMIC = 2; final String name; - final String sourcePackage; + String sourcePackage; + PackageSettingBase packageSetting; final int type; + int protectionLevel; PackageParser.Permission perm; PermissionInfo pendingInfo; int uid; @@ -5412,6 +7264,14 @@ class PackageManagerService extends IPackageManager.Stub { name = _name; sourcePackage = _sourcePackage; type = _type; + // Default to most conservative protection level. + protectionLevel = PermissionInfo.PROTECTION_SIGNATURE; + } + + public String toString() { + return "BasePermission{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + name + "}"; } } @@ -5549,101 +7409,6 @@ class PackageManagerService extends IPackageManager.Stub { } } - /** - * If any of the given 'sigs' is contained in the existing signatures, - * then completely replace the current signatures with the ones in - * 'sigs'. This is used for updating an existing package to a newly - * installed version. - */ - boolean updateSignatures(Signature[] sigs, boolean update) { - if (mSignatures == null) { - if (update) { - assignSignatures(sigs); - } - return true; - } - if (sigs == null) { - return false; - } - - for (int i=0; i<sigs.length; i++) { - Signature sig = sigs[i]; - for (int j=0; j<mSignatures.length; j++) { - if (mSignatures[j].equals(sig)) { - if (update) { - assignSignatures(sigs); - } - return true; - } - } - } - return false; - } - - /** - * If any of the given 'sigs' is contained in the existing signatures, - * then add in any new signatures found in 'sigs'. This is used for - * including a new package into an existing shared user id. - */ - boolean mergeSignatures(Signature[] sigs, boolean update) { - if (mSignatures == null) { - if (update) { - assignSignatures(sigs); - } - return true; - } - if (sigs == null) { - return false; - } - - Signature[] added = null; - int addedCount = 0; - boolean haveMatch = false; - for (int i=0; i<sigs.length; i++) { - Signature sig = sigs[i]; - boolean found = false; - for (int j=0; j<mSignatures.length; j++) { - if (mSignatures[j].equals(sig)) { - found = true; - haveMatch = true; - break; - } - } - - if (!found) { - if (added == null) { - added = new Signature[sigs.length]; - } - added[i] = sig; - addedCount++; - } - } - - if (!haveMatch) { - // Nothing matched -- reject the new signatures. - return false; - } - if (added == null) { - // Completely matched -- nothing else to do. - return true; - } - - // Add additional signatures in. - if (update) { - Signature[] total = new Signature[addedCount+mSignatures.length]; - System.arraycopy(mSignatures, 0, total, 0, mSignatures.length); - int j = mSignatures.length; - for (int i=0; i<added.length; i++) { - if (added[i] != null) { - total[j] = added[i]; - j++; - } - } - mSignatures = total; - } - return true; - } - private void assignSignatures(Signature[] sigs) { if (sigs == null) { mSignatures = null; @@ -5654,7 +7419,7 @@ class PackageManagerService extends IPackageManager.Stub { mSignatures[i] = sigs[i]; } } - + @Override public String toString() { StringBuffer buf = new StringBuffer(128); @@ -5840,22 +7605,28 @@ class PackageManagerService extends IPackageManager.Stub { static class GrantedPermissions { int pkgFlags; - + HashSet<String> grantedPermissions = new HashSet<String>(); int[] gids; - - HashSet<String> loadedPermissions = new HashSet<String>(); - + GrantedPermissions(int pkgFlags) { - this.pkgFlags = pkgFlags & ApplicationInfo.FLAG_SYSTEM; + setFlags(pkgFlags); + } + + void setFlags(int pkgFlags) { + this.pkgFlags = pkgFlags & ( + ApplicationInfo.FLAG_SYSTEM | + ApplicationInfo.FLAG_FORWARD_LOCK | + ApplicationInfo.FLAG_EXTERNAL_STORAGE); } } - + /** * Settings base class for pending and resolved classes. */ static class PackageSettingBase extends GrantedPermissions { final String name; + final String realName; File codePath; String codePathString; File resourcePath; @@ -5864,47 +7635,57 @@ class PackageManagerService extends IPackageManager.Stub { private String timeStampString = "0"; int versionCode; + boolean uidError; + PackageSignatures signatures = new PackageSignatures(); boolean permissionsFixed; - + boolean haveGids; + /* Explicitly disabled components */ HashSet<String> disabledComponents = new HashSet<String>(0); /* Explicitly enabled components */ HashSet<String> enabledComponents = new HashSet<String>(0); int enabled = COMPONENT_ENABLED_STATE_DEFAULT; int installStatus = PKG_INSTALL_COMPLETE; + + PackageSettingBase origPackage; /* package name of the app that installed this package */ String installerPackageName; - PackageSettingBase(String name, File codePath, File resourcePath, + PackageSettingBase(String name, String realName, File codePath, File resourcePath, int pVersionCode, int pkgFlags) { super(pkgFlags); this.name = name; + this.realName = realName; + init(codePath, resourcePath, pVersionCode); + } + + void init(File codePath, File resourcePath, int pVersionCode) { this.codePath = codePath; this.codePathString = codePath.toString(); this.resourcePath = resourcePath; this.resourcePathString = resourcePath.toString(); this.versionCode = pVersionCode; } - + public void setInstallerPackageName(String packageName) { installerPackageName = packageName; } - + String getInstallerPackageName() { return installerPackageName; } - + public void setInstallStatus(int newStatus) { installStatus = newStatus; } - + public int getInstallStatus() { return installStatus; } - + public void setTimeStamp(long newStamp) { if (newStamp != timeStamp) { timeStamp = newStamp; @@ -5916,11 +7697,11 @@ class PackageManagerService extends IPackageManager.Stub { timeStamp = newStamp; timeStampString = newStampStr; } - + public long getTimeStamp() { return timeStamp; } - + public String getTimeStampStr() { return timeStampString; } @@ -5928,31 +7709,34 @@ class PackageManagerService extends IPackageManager.Stub { public void copyFrom(PackageSettingBase base) { grantedPermissions = base.grantedPermissions; gids = base.gids; - loadedPermissions = base.loadedPermissions; - + timeStamp = base.timeStamp; timeStampString = base.timeStampString; signatures = base.signatures; permissionsFixed = base.permissionsFixed; + haveGids = base.haveGids; disabledComponents = base.disabledComponents; enabledComponents = base.enabledComponents; enabled = base.enabled; installStatus = base.installStatus; } - void enableComponentLP(String componentClassName) { - disabledComponents.remove(componentClassName); - enabledComponents.add(componentClassName); + boolean enableComponentLP(String componentClassName) { + boolean changed = disabledComponents.remove(componentClassName); + changed |= enabledComponents.add(componentClassName); + return changed; } - void disableComponentLP(String componentClassName) { - enabledComponents.remove(componentClassName); - disabledComponents.add(componentClassName); + boolean disableComponentLP(String componentClassName) { + boolean changed = enabledComponents.remove(componentClassName); + changed |= disabledComponents.add(componentClassName); + return changed; } - void restoreComponentLP(String componentClassName) { - enabledComponents.remove(componentClassName); - disabledComponents.remove(componentClassName); + boolean restoreComponentLP(String componentClassName) { + boolean changed = enabledComponents.remove(componentClassName); + changed |= disabledComponents.remove(componentClassName); + return changed; } int currentEnabledStateLP(String componentName) { @@ -5974,11 +7758,11 @@ class PackageManagerService extends IPackageManager.Stub { PackageParser.Package pkg; SharedUserSetting sharedUser; - PackageSetting(String name, File codePath, File resourcePath, + PackageSetting(String name, String realName, File codePath, File resourcePath, int pVersionCode, int pkgFlags) { - super(name, codePath, resourcePath, pVersionCode, pkgFlags); + super(name, realName, codePath, resourcePath, pVersionCode, pkgFlags); } - + @Override public String toString() { return "PackageSetting{" @@ -6000,7 +7784,7 @@ class PackageManagerService extends IPackageManager.Stub { super(_pkgFlags); name = _name; } - + @Override public String toString() { return "SharedUserSetting{" @@ -6015,17 +7799,28 @@ class PackageManagerService extends IPackageManager.Stub { private static final class Settings { private final File mSettingsFilename; private final File mBackupSettingsFilename; + private final File mPackageListFilename; private final HashMap<String, PackageSetting> mPackages = new HashMap<String, PackageSetting>(); // List of replaced system applications final HashMap<String, PackageSetting> mDisabledSysPackages = new HashMap<String, PackageSetting>(); + + // These are the last platform API version we were using for + // the apps installed on internal and external storage. It is + // used to grant newer permissions one time during a system upgrade. + int mInternalSdkPlatform; + int mExternalSdkPlatform; // The user's preferred activities associated with particular intent // filters. private final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities = new IntentResolver<PreferredActivity, PreferredActivity>() { @Override + protected String packageForFilter(PreferredActivity filter) { + return filter.mActivity.getPackageName(); + } + @Override protected void dumpFilter(PrintWriter out, String prefix, PreferredActivity filter) { out.print(prefix); out.print( @@ -6061,14 +7856,24 @@ class PackageManagerService extends IPackageManager.Stub { final HashMap<String, BasePermission> mPermissionTrees = new HashMap<String, BasePermission>(); + // Packages that have been uninstalled and still need their external + // storage data deleted. + final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>(); + + // Packages that have been renamed since they were first installed. + // Keys are the new names of the packages, values are the original + // names. The packages appear everwhere else under their original + // names. + final HashMap<String, String> mRenamedPackages = new HashMap<String, String>(); + private final StringBuilder mReadMessages = new StringBuilder(); private static final class PendingPackage extends PackageSettingBase { final int sharedId; - PendingPackage(String name, File codePath, File resourcePath, + PendingPackage(String name, String realName, File codePath, File resourcePath, int sharedId, int pVersionCode, int pkgFlags) { - super(name, codePath, resourcePath, pVersionCode, pkgFlags); + super(name, realName, codePath, resourcePath, pVersionCode, pkgFlags); this.sharedId = sharedId; } } @@ -6078,6 +7883,7 @@ class PackageManagerService extends IPackageManager.Stub { Settings() { File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); + // TODO(oam): This secure dir creation needs to be moved somewhere else (later) systemDir.mkdirs(); FileUtils.setPermissions(systemDir.toString(), FileUtils.S_IRWXU|FileUtils.S_IRWXG @@ -6085,17 +7891,18 @@ class PackageManagerService extends IPackageManager.Stub { -1, -1); mSettingsFilename = new File(systemDir, "packages.xml"); mBackupSettingsFilename = new File(systemDir, "packages-backup.xml"); + mPackageListFilename = new File(systemDir, "packages.list"); } - PackageSetting getPackageLP(PackageParser.Package pkg, - SharedUserSetting sharedUser, File codePath, File resourcePath, + PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage, + String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, int pkgFlags, boolean create, boolean add) { final String name = pkg.packageName; - PackageSetting p = getPackageLP(name, sharedUser, codePath, + PackageSetting p = getPackageLP(name, origPackage, realName, sharedUser, codePath, resourcePath, pkg.mVersionCode, pkgFlags, create, add); return p; } - + PackageSetting peekPackageLP(String name) { return mPackages.get(name); /* @@ -6106,7 +7913,7 @@ class PackageManagerService extends IPackageManager.Stub { return null; */ } - + void setInstallStatus(String pkgName, int status) { PackageSetting p = mPackages.get(pkgName); if(p != null) { @@ -6115,7 +7922,7 @@ class PackageManagerService extends IPackageManager.Stub { } } } - + void setInstallerPackageName(String pkgName, String installerPkgName) { PackageSetting p = mPackages.get(pkgName); @@ -6123,17 +7930,17 @@ class PackageManagerService extends IPackageManager.Stub { p.setInstallerPackageName(installerPkgName); } } - + String getInstallerPackageName(String pkgName) { PackageSetting p = mPackages.get(pkgName); - return (p == null) ? null : p.getInstallerPackageName(); + return (p == null) ? null : p.getInstallerPackageName(); } int getInstallStatus(String pkgName) { PackageSetting p = mPackages.get(pkgName); if(p != null) { return p.getInstallStatus(); - } + } return -1; } @@ -6177,7 +7984,7 @@ class PackageManagerService extends IPackageManager.Stub { } return removePackageLP(name); } - + PackageSetting enableSystemPackageLP(String name) { PackageSetting p = mDisabledSysPackages.get(name); if(p == null) { @@ -6188,13 +7995,13 @@ class PackageManagerService extends IPackageManager.Stub { if((p.pkg != null) && (p.pkg.applicationInfo != null)) { p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } - PackageSetting ret = addPackageLP(name, p.codePath, + PackageSetting ret = addPackageLP(name, p.realName, p.codePath, p.resourcePath, p.userId, p.versionCode, p.pkgFlags); mDisabledSysPackages.remove(name); return ret; } - - PackageSetting addPackageLP(String name, File codePath, + + PackageSetting addPackageLP(String name, String realName, File codePath, File resourcePath, int uid, int vc, int pkgFlags) { PackageSetting p = mPackages.get(name); if (p != null) { @@ -6205,7 +8012,7 @@ class PackageManagerService extends IPackageManager.Stub { "Adding duplicate package, keeping first: " + name); return null; } - p = new PackageSetting(name, codePath, resourcePath, vc, pkgFlags); + p = new PackageSetting(name, realName, codePath, resourcePath, vc, pkgFlags); p.userId = uid; if (addUserIdLP(uid, p, name)) { mPackages.put(name, p); @@ -6233,8 +8040,33 @@ class PackageManagerService extends IPackageManager.Stub { return null; } - private PackageSetting getPackageLP(String name, - SharedUserSetting sharedUser, File codePath, File resourcePath, + // Transfer ownership of permissions from one package to another. + private void transferPermissions(String origPkg, String newPkg) { + // Transfer ownership of permissions to the new package. + for (int i=0; i<2; i++) { + HashMap<String, BasePermission> permissions = + i == 0 ? mPermissionTrees : mPermissions; + for (BasePermission bp : permissions.values()) { + if (origPkg.equals(bp.sourcePackage)) { + if (DEBUG_UPGRADE) Log.v(TAG, + "Moving permission " + bp.name + + " from pkg " + bp.sourcePackage + + " to " + newPkg); + bp.sourcePackage = newPkg; + bp.packageSetting = null; + bp.perm = null; + if (bp.pendingInfo != null) { + bp.pendingInfo.packageName = newPkg; + } + bp.uid = 0; + bp.gids = null; + } + } + } + } + + private PackageSetting getPackageLP(String name, PackageSetting origPackage, + String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, int vc, int pkgFlags, boolean create, boolean add) { PackageSetting p = mPackages.get(name); if (p != null) { @@ -6244,12 +8076,12 @@ class PackageManagerService extends IPackageManager.Stub { // This is an updated system app with versions in both system // and data partition. Just let the most recent version // take precedence. - Log.w(TAG, "Trying to update system app code path from " + + Slog.w(TAG, "Trying to update system app code path from " + p.codePathString + " to " + codePath.toString()); } else { - // Let the app continue with previous uid if code path changes. - reportSettingsProblem(Log.WARN, - "Package " + name + " codePath changed from " + p.codePath + // Just a change in the code path is not an issue, but + // let's log a message about it. + Slog.i(TAG, "Package " + name + " codePath changed from " + p.codePath + " to " + codePath + "; Retaining data and using new"); } } @@ -6276,37 +8108,56 @@ class PackageManagerService extends IPackageManager.Stub { if (!create) { return null; } - p = new PackageSetting(name, codePath, resourcePath, vc, pkgFlags); - p.setTimeStamp(codePath.lastModified()); - p.sharedUser = sharedUser; - if (sharedUser != null) { - p.userId = sharedUser.userId; - } else if (MULTIPLE_APPLICATION_UIDS) { - // Clone the setting here for disabled system packages - PackageSetting dis = mDisabledSysPackages.get(name); - if (dis != null) { - // For disabled packages a new setting is created - // from the existing user id. This still has to be - // added to list of user id's - // Copy signatures from previous setting - if (dis.signatures.mSignatures != null) { - p.signatures.mSignatures = dis.signatures.mSignatures.clone(); + if (origPackage != null) { + // We are consuming the data from an existing package. + p = new PackageSetting(origPackage.name, name, codePath, + resourcePath, vc, pkgFlags); + if (DEBUG_UPGRADE) Log.v(TAG, "Package " + name + + " is adopting original package " + origPackage.name); + // Note that we will retain the new package's signature so + // that we can keep its data. + PackageSignatures s = p.signatures; + p.copyFrom(origPackage); + p.signatures = s; + p.sharedUser = origPackage.sharedUser; + p.userId = origPackage.userId; + p.origPackage = origPackage; + mRenamedPackages.put(name, origPackage.name); + name = origPackage.name; + // Update new package state. + p.setTimeStamp(codePath.lastModified()); + } else { + p = new PackageSetting(name, realName, codePath, resourcePath, vc, pkgFlags); + p.setTimeStamp(codePath.lastModified()); + p.sharedUser = sharedUser; + if (sharedUser != null) { + p.userId = sharedUser.userId; + } else if (MULTIPLE_APPLICATION_UIDS) { + // Clone the setting here for disabled system packages + PackageSetting dis = mDisabledSysPackages.get(name); + if (dis != null) { + // For disabled packages a new setting is created + // from the existing user id. This still has to be + // added to list of user id's + // Copy signatures from previous setting + if (dis.signatures.mSignatures != null) { + p.signatures.mSignatures = dis.signatures.mSignatures.clone(); + } + p.userId = dis.userId; + // Clone permissions + p.grantedPermissions = new HashSet<String>(dis.grantedPermissions); + // Clone component info + p.disabledComponents = new HashSet<String>(dis.disabledComponents); + p.enabledComponents = new HashSet<String>(dis.enabledComponents); + // Add new setting to list of user ids + addUserIdLP(p.userId, p, name); + } else { + // Assign new user id + p.userId = newUserIdLP(p); } - p.userId = dis.userId; - // Clone permissions - p.grantedPermissions = new HashSet<String>(dis.grantedPermissions); - p.loadedPermissions = new HashSet<String>(dis.loadedPermissions); - // Clone component info - p.disabledComponents = new HashSet<String>(dis.disabledComponents); - p.enabledComponents = new HashSet<String>(dis.enabledComponents); - // Add new setting to list of user ids - addUserIdLP(p.userId, p, name); } else { - // Assign new user id - p.userId = newUserIdLP(p); + p.userId = FIRST_APPLICATION_UID; } - } else { - p.userId = FIRST_APPLICATION_UID; } if (p.userId < 0) { reportSettingsProblem(Log.WARN, @@ -6314,7 +8165,7 @@ class PackageManagerService extends IPackageManager.Stub { return null; } if (add) { - // Finish adding new package by adding it and updating shared + // Finish adding new package by adding it and updating shared // user preferences addPackageSettingLP(p, name, sharedUser); } @@ -6322,27 +8173,37 @@ class PackageManagerService extends IPackageManager.Stub { return p; } - private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg, - File codePath, File resourcePath) { + private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) { p.pkg = pkg; + String codePath = pkg.applicationInfo.sourceDir; + String resourcePath = pkg.applicationInfo.publicSourceDir; // Update code path if needed - if (!codePath.toString().equalsIgnoreCase(p.codePathString)) { - Log.w(TAG, "Code path for pkg : " + p.pkg.packageName + + if (!codePath.equalsIgnoreCase(p.codePathString)) { + Slog.w(TAG, "Code path for pkg : " + p.pkg.packageName + " changing from " + p.codePathString + " to " + codePath); - p.codePath = codePath; - p.codePathString = codePath.toString(); + p.codePath = new File(codePath); + p.codePathString = codePath; } //Update resource path if needed - if (!resourcePath.toString().equalsIgnoreCase(p.resourcePathString)) { - Log.w(TAG, "Resource path for pkg : " + p.pkg.packageName + + if (!resourcePath.equalsIgnoreCase(p.resourcePathString)) { + Slog.w(TAG, "Resource path for pkg : " + p.pkg.packageName + " changing from " + p.resourcePathString + " to " + resourcePath); - p.resourcePath = resourcePath; - p.resourcePathString = resourcePath.toString(); + p.resourcePath = new File(resourcePath); + p.resourcePathString = resourcePath; } // Update version code if needed if (pkg.mVersionCode != p.versionCode) { p.versionCode = pkg.mVersionCode; } + // Update signatures if needed. + if (p.signatures.mSignatures == null) { + p.signatures.assignSignatures(pkg.mSignatures); + } + // If this app defines a shared user id initialize + // the shared user signatures as well. + if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) { + p.sharedUser.signatures.assignSignatures(pkg.mSignatures); + } addPackageSettingLP(p, pkg.packageName, p.sharedUser); } @@ -6382,7 +8243,7 @@ class PackageManagerService extends IPackageManager.Stub { */ private void updateSharedUserPermsLP(PackageSetting deletedPs, int[] globalGids) { if ( (deletedPs == null) || (deletedPs.pkg == null)) { - Log.i(TAG, "Trying to update info for null package. Just ignoring"); + Slog.i(TAG, "Trying to update info for null package. Just ignoring"); return; } // No sharedUserId @@ -6398,7 +8259,7 @@ class PackageManagerService extends IPackageManager.Stub { } for (PackageSetting pkg:sus.packages) { if (pkg.pkg != null && - !pkg.pkg.packageName.equalsIgnoreCase(deletedPs.pkg.packageName) && + !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) && pkg.pkg.requestedPermissions.contains(eachPerm)) { used = true; break; @@ -6407,7 +8268,6 @@ class PackageManagerService extends IPackageManager.Stub { if (!used) { // can safely delete this permission from list sus.grantedPermissions.remove(eachPerm); - sus.loadedPermissions.remove(eachPerm); } } // Update gids @@ -6481,6 +8341,17 @@ class PackageManagerService extends IPackageManager.Stub { } } + private Set<String> findPackagesWithFlag(int flag) { + Set<String> ret = new HashSet<String>(); + for (PackageSetting ps : mPackages.values()) { + // Has to match atleast all the flag bits set on flag + if ((ps.pkgFlags & flag) == flag) { + ret.add(ps.name); + } + } + return ret; + } + private void removeUserIdLP(int uid) { if (uid >= FIRST_APPLICATION_UID) { int N = mUserIds.size(); @@ -6490,7 +8361,7 @@ class PackageManagerService extends IPackageManager.Stub { mOtherUserIds.remove(uid); } } - + void writeLP() { //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024); @@ -6503,11 +8374,12 @@ class PackageManagerService extends IPackageManager.Stub { // might have been corrupted. if (!mBackupSettingsFilename.exists()) { if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) { - Log.w(TAG, "Unable to backup package manager settings, current changes will be lost at reboot"); + Slog.w(TAG, "Unable to backup package manager settings, current changes will be lost at reboot"); return; } } else { - Log.w(TAG, "Preserving older settings backup"); + mSettingsFilename.delete(); + Slog.w(TAG, "Preserving older settings backup"); } } @@ -6524,6 +8396,11 @@ class PackageManagerService extends IPackageManager.Stub { serializer.startTag(null, "packages"); + serializer.startTag(null, "last-platform-version"); + serializer.attribute(null, "internal", Integer.toString(mInternalSdkPlatform)); + serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform)); + serializer.endTag(null, "last-platform-version"); + serializer.startTag(null, "permission-trees"); for (BasePermission bp : mPermissionTrees.values()) { writePermission(serializer, bp); @@ -6539,7 +8416,7 @@ class PackageManagerService extends IPackageManager.Stub { for (PackageSetting pkg : mPackages.values()) { writePackage(serializer, pkg); } - + for (PackageSetting pkg : mDisabledSysPackages.values()) { writeDisabledSysPackage(serializer, pkg); } @@ -6568,6 +8445,23 @@ class PackageManagerService extends IPackageManager.Stub { serializer.endTag(null, "shared-user"); } + if (mPackagesToBeCleaned.size() > 0) { + for (int i=0; i<mPackagesToBeCleaned.size(); i++) { + serializer.startTag(null, "cleaning-package"); + serializer.attribute(null, "name", mPackagesToBeCleaned.get(i)); + serializer.endTag(null, "cleaning-package"); + } + } + + if (mRenamedPackages.size() > 0) { + for (HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) { + serializer.startTag(null, "renamed-package"); + serializer.attribute(null, "new", e.getKey()); + serializer.attribute(null, "old", e.getValue()); + serializer.endTag(null, "renamed-package"); + } + } + serializer.endTag(null, "packages"); serializer.endDocument(); @@ -6583,14 +8477,69 @@ class PackageManagerService extends IPackageManager.Stub { |FileUtils.S_IRGRP|FileUtils.S_IWGRP |FileUtils.S_IROTH, -1, -1); + + // Write package list file now, use a JournaledFile. + // + File tempFile = new File(mPackageListFilename.toString() + ".tmp"); + JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile); + + str = new FileOutputStream(journal.chooseForWrite()); + try { + StringBuilder sb = new StringBuilder(); + for (PackageSetting pkg : mPackages.values()) { + ApplicationInfo ai = pkg.pkg.applicationInfo; + String dataPath = ai.dataDir; + boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + + // Avoid any application that has a space in its path + // or that is handled by the system. + if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID) + continue; + + // we store on each line the following information for now: + // + // pkgName - package name + // userId - application-specific user id + // debugFlag - 0 or 1 if the package is debuggable. + // dataPath - path to package's data path + // + // NOTE: We prefer not to expose all ApplicationInfo flags for now. + // + // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS + // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES: + // system/core/run-as/run-as.c + // + sb.setLength(0); + sb.append(ai.packageName); + sb.append(" "); + sb.append((int)ai.uid); + sb.append(isDebug ? " 1 " : " 0 "); + sb.append(dataPath); + sb.append("\n"); + str.write(sb.toString().getBytes()); + } + str.flush(); + str.close(); + journal.commit(); + } + catch (Exception e) { + journal.rollback(); + } + + FileUtils.setPermissions(mPackageListFilename.toString(), + FileUtils.S_IRUSR|FileUtils.S_IWUSR + |FileUtils.S_IRGRP|FileUtils.S_IWGRP + |FileUtils.S_IROTH, + -1, -1); + return; } catch(XmlPullParserException e) { - Log.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); + Slog.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); } catch(java.io.IOException e) { - Log.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); + Slog.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); } - // Clean up partially written file + // Clean up partially written files if (mSettingsFilename.exists()) { if (!mSettingsFilename.delete()) { Log.i(TAG, "Failed to clean up mangled file: " + mSettingsFilename); @@ -6598,11 +8547,14 @@ class PackageManagerService extends IPackageManager.Stub { } //Debug.stopMethodTracing(); } - - void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg) - throws java.io.IOException { + + void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg) + throws java.io.IOException { serializer.startTag(null, "updated-package"); serializer.attribute(null, "name", pkg.name); + if (pkg.realName != null) { + serializer.attribute(null, "realName", pkg.realName); + } serializer.attribute(null, "codePath", pkg.codePathString); serializer.attribute(null, "ts", pkg.getTimeStampStr()); serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); @@ -6624,7 +8576,7 @@ class PackageManagerService extends IPackageManager.Stub { // be set. for (final String name : pkg.grantedPermissions) { BasePermission bp = mPermissions.get(name); - if ((bp != null) && (bp.perm != null) && (bp.perm.info != null)) { + if (bp != 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"); @@ -6636,18 +8588,20 @@ class PackageManagerService extends IPackageManager.Stub { serializer.endTag(null, "perms"); serializer.endTag(null, "updated-package"); } - - void writePackage(XmlSerializer serializer, final PackageSetting pkg) - throws java.io.IOException { + + void writePackage(XmlSerializer serializer, final PackageSetting pkg) + throws java.io.IOException { serializer.startTag(null, "package"); serializer.attribute(null, "name", pkg.name); + if (pkg.realName != null) { + serializer.attribute(null, "realName", pkg.realName); + } serializer.attribute(null, "codePath", pkg.codePathString); if (!pkg.resourcePathString.equals(pkg.codePathString)) { serializer.attribute(null, "resourcePath", pkg.resourcePathString); } - serializer.attribute(null, "system", - (pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0 - ? "true" : "false"); + serializer.attribute(null, "flags", + Integer.toString(pkg.pkgFlags)); serializer.attribute(null, "ts", pkg.getTimeStampStr()); serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); if (pkg.sharedUser == null) { @@ -6657,6 +8611,9 @@ class PackageManagerService extends IPackageManager.Stub { serializer.attribute(null, "sharedUserId", Integer.toString(pkg.userId)); } + if (pkg.uidError) { + serializer.attribute(null, "uidError", "true"); + } if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { serializer.attribute(null, "enabled", pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED @@ -6702,10 +8659,10 @@ class PackageManagerService extends IPackageManager.Stub { } serializer.endTag(null, "enabled-components"); } - + serializer.endTag(null, "package"); } - + void writePermission(XmlSerializer serializer, BasePermission bp) throws XmlPullParserException, java.io.IOException { if (bp.type != BasePermission.TYPE_BUILTIN @@ -6713,6 +8670,11 @@ class PackageManagerService extends IPackageManager.Stub { serializer.startTag(null, "item"); serializer.attribute(null, "name", bp.name); serializer.attribute(null, "package", bp.sourcePackage); + if (bp.protectionLevel != + PermissionInfo.PROTECTION_NORMAL) { + serializer.attribute(null, "protection", + Integer.toString(bp.protectionLevel)); + } if (DEBUG_SETTINGS) Log.v(TAG, "Writing perm: name=" + bp.name + " type=" + bp.type); if (bp.type == BasePermission.TYPE_DYNAMIC) { @@ -6728,11 +8690,6 @@ class PackageManagerService extends IPackageManager.Stub { serializer.attribute(null, "label", pi.nonLocalizedLabel.toString()); } - if (pi.protectionLevel != - PermissionInfo.PROTECTION_NORMAL) { - serializer.attribute(null, "protection", - Integer.toString(pi.protectionLevel)); - } } } serializer.endTag(null, "item"); @@ -6756,7 +8713,7 @@ class PackageManagerService extends IPackageManager.Stub { } return ret; } - + boolean readLP() { FileInputStream str = null; if (mBackupSettingsFilename.exists()) { @@ -6768,7 +8725,7 @@ class PackageManagerService extends IPackageManager.Stub { // If both the backup and settings file exist, we // ignore the settings since it might have been // corrupted. - Log.w(TAG, "Cleaning up settings file " + mSettingsFilename); + Slog.w(TAG, "Cleaning up settings file " + mSettingsFilename); mSettingsFilename.delete(); } } catch (java.io.IOException e) { @@ -6782,7 +8739,7 @@ class PackageManagerService extends IPackageManager.Stub { if (str == null) { if (!mSettingsFilename.exists()) { mReadMessages.append("No settings file found\n"); - Log.i(TAG, "No current settings file!"); + Slog.i(TAG, "No current settings file!"); return false; } str = new FileInputStream(mSettingsFilename); @@ -6798,7 +8755,7 @@ class PackageManagerService extends IPackageManager.Stub { if (type != XmlPullParser.START_TAG) { mReadMessages.append("No start tag found in settings file\n"); - Log.e(TAG, "No start tag found in package manager settings"); + Slog.e(TAG, "No start tag found in package manager settings"); return false; } @@ -6826,8 +8783,32 @@ class PackageManagerService extends IPackageManager.Stub { readPreferredActivitiesLP(parser); } else if(tagName.equals("updated-package")) { readDisabledSysPackageLP(parser); + } else if (tagName.equals("cleaning-package")) { + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + mPackagesToBeCleaned.add(name); + } + } else if (tagName.equals("renamed-package")) { + String nname = parser.getAttributeValue(null, "new"); + String oname = parser.getAttributeValue(null, "old"); + if (nname != null && oname != null) { + mRenamedPackages.put(nname, oname); + } + } else if (tagName.equals("last-platform-version")) { + mInternalSdkPlatform = mExternalSdkPlatform = 0; + try { + String internal = parser.getAttributeValue(null, "internal"); + if (internal != null) { + mInternalSdkPlatform = Integer.parseInt(internal); + } + String external = parser.getAttributeValue(null, "external"); + if (external != null) { + mExternalSdkPlatform = Integer.parseInt(external); + } + } catch (NumberFormatException e) { + } } else { - Log.w(TAG, "Unknown element under <packages>: " + Slog.w(TAG, "Unknown element under <packages>: " + parser.getName()); XmlUtils.skipCurrentTag(parser); } @@ -6837,11 +8818,11 @@ class PackageManagerService extends IPackageManager.Stub { } catch(XmlPullParserException e) { mReadMessages.append("Error reading: " + e.toString()); - Log.e(TAG, "Error reading package manager settings", e); + Slog.e(TAG, "Error reading package manager settings", e); } catch(java.io.IOException e) { mReadMessages.append("Error reading: " + e.toString()); - Log.e(TAG, "Error reading package manager settings", e); + Slog.e(TAG, "Error reading package manager settings", e); } @@ -6850,11 +8831,11 @@ class PackageManagerService extends IPackageManager.Stub { final PendingPackage pp = mPendingPackages.get(i); Object idObj = getUserIdLP(pp.sharedId); if (idObj != null && idObj instanceof SharedUserSetting) { - PackageSetting p = getPackageLP(pp.name, + PackageSetting p = getPackageLP(pp.name, null, pp.realName, (SharedUserSetting)idObj, pp.codePath, pp.resourcePath, pp.versionCode, pp.pkgFlags, true, true); if (p == null) { - Log.w(TAG, "Unable to create application package for " + Slog.w(TAG, "Unable to create application package for " + pp.name); continue; } @@ -6864,13 +8845,13 @@ class PackageManagerService extends IPackageManager.Stub { + " has shared uid " + pp.sharedId + " that is not a shared uid\n"; mReadMessages.append(msg); - Log.e(TAG, msg); + Slog.e(TAG, msg); } else { String msg = "Bad package setting: package " + pp.name + " has shared uid " + pp.sharedId + " that is not defined\n"; mReadMessages.append(msg); - Log.e(TAG, msg); + Slog.e(TAG, msg); } } mPendingPackages.clear(); @@ -6923,6 +8904,8 @@ class PackageManagerService extends IPackageManager.Stub { dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL); + bp.protectionLevel = readInt(parser, null, "protection", + PermissionInfo.PROTECTION_NORMAL); if (dynamic) { PermissionInfo pi = new PermissionInfo(); pi.packageName = sourcePackage.intern(); @@ -6930,8 +8913,7 @@ class PackageManagerService extends IPackageManager.Stub { pi.icon = readInt(parser, null, "icon", 0); pi.nonLocalizedLabel = parser.getAttributeValue( null, "label"); - pi.protectionLevel = readInt(parser, null, "protection", - PermissionInfo.PROTECTION_NORMAL); + pi.protectionLevel = bp.protectionLevel; bp.pendingInfo = pi; } out.put(bp.name, bp); @@ -6949,13 +8931,14 @@ class PackageManagerService extends IPackageManager.Stub { XmlUtils.skipCurrentTag(parser); } } - + private void readDisabledSysPackageLP(XmlPullParser parser) - throws XmlPullParserException, IOException { + throws XmlPullParserException, IOException { String name = parser.getAttributeValue(null, "name"); + String realName = parser.getAttributeValue(null, "realName"); String codePathStr = parser.getAttributeValue(null, "codePath"); String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); - if(resourcePathStr == null) { + if (resourcePathStr == null) { resourcePathStr = codePathStr; } String version = parser.getAttributeValue(null, "version"); @@ -6966,11 +8949,11 @@ class PackageManagerService extends IPackageManager.Stub { } catch (NumberFormatException e) { } } - + int pkgFlags = 0; pkgFlags |= ApplicationInfo.FLAG_SYSTEM; - PackageSetting ps = new PackageSetting(name, - new File(codePathStr), + PackageSetting ps = new PackageSetting(name, realName, + new File(codePathStr), new File(resourcePathStr), versionCode, pkgFlags); String timeStampStr = parser.getAttributeValue(null, "ts"); if (timeStampStr != null) { @@ -7009,16 +8992,18 @@ class PackageManagerService extends IPackageManager.Stub { } mDisabledSysPackages.put(name, ps); } - + private void readPackageLP(XmlPullParser parser) throws XmlPullParserException, IOException { String name = null; + String realName = null; String idStr = null; String sharedIdStr = null; String codePathStr = null; String resourcePathStr = null; String systemStr = null; String installerPackageName = null; + String uidError = null; int pkgFlags = 0; String timeStampStr; long timeStamp = 0; @@ -7027,7 +9012,9 @@ class PackageManagerService extends IPackageManager.Stub { int versionCode = 0; try { name = parser.getAttributeValue(null, "name"); + realName = parser.getAttributeValue(null, "realName"); idStr = parser.getAttributeValue(null, "userId"); + uidError = parser.getAttributeValue(null, "uidError"); sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); codePathStr = parser.getAttributeValue(null, "codePath"); resourcePathStr = parser.getAttributeValue(null, "resourcePath"); @@ -7038,16 +9025,24 @@ class PackageManagerService extends IPackageManager.Stub { } catch (NumberFormatException e) { } } - systemStr = parser.getAttributeValue(null, "system"); installerPackageName = parser.getAttributeValue(null, "installer"); + + systemStr = parser.getAttributeValue(null, "flags"); if (systemStr != null) { - if ("true".equals(systemStr)) { - pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + try { + pkgFlags = Integer.parseInt(systemStr); + } catch (NumberFormatException e) { } } else { - // Old settings that don't specify system... just treat - // them as system, good enough. - pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + // For backward compatibility + systemStr = parser.getAttributeValue(null, "system"); + if (systemStr != null) { + pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM : 0; + } else { + // Old settings that don't specify system... just treat + // them as system, good enough. + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + } } timeStampStr = parser.getAttributeValue(null, "ts"); if (timeStampStr != null) { @@ -7062,6 +9057,9 @@ class PackageManagerService extends IPackageManager.Stub { if (resourcePathStr == null) { resourcePathStr = codePathStr; } + if (realName != null) { + realName = realName.intern(); + } if (name == null) { reportSettingsProblem(Log.WARN, "Error in package manager settings: <package> has no name at " @@ -7071,8 +9069,9 @@ class PackageManagerService extends IPackageManager.Stub { "Error in package manager settings: <package> has no codePath at " + parser.getPositionDescription()); } else if (userId > 0) { - packageSetting = addPackageLP(name.intern(), new File(codePathStr), - new File(resourcePathStr), userId, versionCode, pkgFlags); + packageSetting = addPackageLP(name.intern(), realName, + new File(codePathStr), new File(resourcePathStr), + userId, versionCode, pkgFlags); if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name + ": userId=" + userId + " pkg=" + packageSetting); if (packageSetting == null) { @@ -7087,8 +9086,9 @@ class PackageManagerService extends IPackageManager.Stub { userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; if (userId > 0) { - packageSetting = new PendingPackage(name.intern(), new File(codePathStr), - new File(resourcePathStr), userId, versionCode, pkgFlags); + packageSetting = new PendingPackage(name.intern(), realName, + new File(codePathStr), new File(resourcePathStr), + userId, versionCode, pkgFlags); packageSetting.setTimeStamp(timeStamp, timeStampStr); mPendingPackages.add((PendingPackage) packageSetting); if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name @@ -7113,6 +9113,7 @@ class PackageManagerService extends IPackageManager.Stub { + parser.getPositionDescription()); } if (packageSetting != null) { + packageSetting.uidError = "true".equals(uidError); packageSetting.installerPackageName = installerPackageName; final String enabledStr = parser.getAttributeValue(null, "enabled"); if (enabledStr != null) { @@ -7139,7 +9140,7 @@ class PackageManagerService extends IPackageManager.Stub { packageSetting.installStatus = PKG_INSTALL_COMPLETE; } } - + int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT @@ -7159,7 +9160,7 @@ class PackageManagerService extends IPackageManager.Stub { packageSetting.signatures.readXml(parser, mPastSignatures); } else if (tagName.equals("perms")) { readGrantedPermissionsLP(parser, - packageSetting.loadedPermissions); + packageSetting.grantedPermissions); packageSetting.permissionsFixed = true; } else { reportSettingsProblem(Log.WARN, @@ -7288,7 +9289,7 @@ class PackageManagerService extends IPackageManager.Stub { if (tagName.equals("sigs")) { su.signatures.readXml(parser, mPastSignatures); } else if (tagName.equals("perms")) { - readGrantedPermissionsLP(parser, su.loadedPermissions); + readGrantedPermissionsLP(parser, su.grantedPermissions); } else { reportSettingsProblem(Log.WARN, "Unknown element under <shared-user>: " @@ -7384,7 +9385,7 @@ class PackageManagerService extends IPackageManager.Stub { mUserIds.add(obj); return FIRST_APPLICATION_UID + N; } - + public PackageSetting getDisabledSystemPkg(String name) { synchronized(mPackages) { PackageSetting ps = mDisabledSysPackages.get(name); @@ -7402,6 +9403,13 @@ class PackageManagerService extends IPackageManager.Stub { Log.v(TAG, "disabledComponents: " + Arrays.toString(packageSettings.disabledComponents.toArray())); } + if (packageSettings == null) { + if (false) { + Log.w(TAG, "WAITING FOR DEBUGGER"); + Debug.waitForDebugger(); + Log.i(TAG, "We will crash!"); + } + } return ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) || ((componentInfo.enabled && ((packageSettings.enabled == COMPONENT_ENABLED_STATE_ENABLED) @@ -7411,4 +9419,536 @@ class PackageManagerService extends IPackageManager.Stub { || packageSettings.enabledComponents.contains(componentInfo.name)); } } + + // ------- apps on sdcard specific code ------- + static final boolean DEBUG_SD_INSTALL = false; + final private String mSdEncryptKey = "AppsOnSD"; + final private String mSdEncryptAlg = "AES"; + private boolean mMediaMounted = false; + private static final int MAX_CONTAINERS = 250; + + private String getEncryptKey() { + try { + String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey); + if (sdEncKey == null) { + sdEncKey = SystemKeyStore.getInstance(). + generateNewKeyHexString(128, mSdEncryptAlg, mSdEncryptKey); + if (sdEncKey == null) { + Slog.e(TAG, "Failed to create encryption keys"); + return null; + } + } + return sdEncKey; + } catch (NoSuchAlgorithmException nsae) { + Slog.e(TAG, "Failed to create encryption keys with exception: " + nsae); + return null; + } + } + + static String getTempContainerId() { + String prefix = "smdl2tmp"; + int tmpIdx = 1; + String list[] = PackageHelper.getSecureContainerList(); + if (list != null) { + int idx = 0; + int idList[] = new int[MAX_CONTAINERS]; + boolean neverFound = true; + for (String name : list) { + // Ignore null entries + if (name == null) { + continue; + } + int sidx = name.indexOf(prefix); + if (sidx == -1) { + // Not a temp file. just ignore + continue; + } + String subStr = name.substring(sidx + prefix.length()); + idList[idx] = -1; + if (subStr != null) { + try { + int cid = Integer.parseInt(subStr); + idList[idx++] = cid; + neverFound = false; + } catch (NumberFormatException e) { + } + } + } + if (!neverFound) { + // Sort idList + Arrays.sort(idList); + for (int j = 1; j <= idList.length; j++) { + if (idList[j-1] != j) { + tmpIdx = j; + break; + } + } + } + } + return prefix + tmpIdx; + } + + /* + * Update media status on PackageManager. + */ + public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Media status can only be updated by the system"); + } + synchronized (mPackages) { + Log.i(TAG, "Updating external media status from " + + (mMediaMounted ? "mounted" : "unmounted") + " to " + + (mediaStatus ? "mounted" : "unmounted")); + if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + + mediaStatus+", mMediaMounted=" + mMediaMounted); + if (mediaStatus == mMediaMounted) { + Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, + reportStatus ? 1 : 0, -1); + mHandler.sendMessage(msg); + return; + } + mMediaMounted = mediaStatus; + } + // Queue up an async operation since the package installation may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + updateExternalMediaStatusInner(mediaStatus, reportStatus); + } + }); + } + + /* + * Collect information of applications on external media, map them + * against existing containers and update information based on current + * mount status. Please note that we always have to report status + * if reportStatus has been set to true especially when unloading packages. + */ + private void updateExternalMediaStatusInner(boolean mediaStatus, + boolean reportStatus) { + // Collection of uids + int uidArr[] = null; + // Collection of stale containers + HashSet<String> removeCids = new HashSet<String>(); + // Collection of packages on external media with valid containers. + HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>(); + // Get list of secure containers. + final String list[] = PackageHelper.getSecureContainerList(); + if (list == null || list.length == 0) { + Log.i(TAG, "No secure containers on sdcard"); + } else { + // Process list of secure containers and categorize them + // as active or stale based on their package internal state. + int uidList[] = new int[list.length]; + int num = 0; + synchronized (mPackages) { + for (String cid : list) { + SdInstallArgs args = new SdInstallArgs(cid); + if (DEBUG_SD_INSTALL) Log.i(TAG, "Processing container " + cid); + String pkgName = args.getPackageName(); + if (pkgName == null) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale"); + removeCids.add(cid); + continue; + } + if (DEBUG_SD_INSTALL) Log.i(TAG, "Looking for pkg : " + pkgName); + PackageSetting ps = mSettings.mPackages.get(pkgName); + if (ps != null && ps.codePathString != null) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + + " corresponds to pkg : " + pkgName + + " at code path: " + ps.codePathString); + // We do have a valid package installed on sdcard + processCids.put(args, ps.codePathString); + int uid = ps.userId; + if (uid != -1) { + uidList[num++] = uid; + } + } else { + // Stale container on sdcard. Just delete + if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale"); + removeCids.add(cid); + } + } + } + + if (num > 0) { + // Sort uid list + Arrays.sort(uidList, 0, num); + // Throw away duplicates + uidArr = new int[num]; + uidArr[0] = uidList[0]; + int di = 0; + for (int i = 1; i < num; i++) { + if (uidList[i-1] != uidList[i]) { + uidArr[di++] = uidList[i]; + } + } + } + } + // Process packages with valid entries. + if (mediaStatus) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); + loadMediaPackages(processCids, uidArr, removeCids); + startCleaningPackages(); + } else { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages"); + unloadMediaPackages(processCids, uidArr, reportStatus); + } + } + + private void sendResourcesChangedBroadcast(boolean mediaStatus, + ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) { + int size = pkgList.size(); + if (size > 0) { + // Send broadcasts here + Bundle extras = new Bundle(); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, + pkgList.toArray(new String[size])); + if (uidArr != null) { + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr); + } + String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE + : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; + sendPackageBroadcast(action, null, extras, finishedReceiver); + } + } + + /* + * Look at potentially valid container ids from processCids + * If package information doesn't match the one on record + * or package scanning fails, the cid is added to list of + * removeCids. We currently don't delete stale containers. + */ + private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, + int uidArr[], HashSet<String> removeCids) { + ArrayList<String> pkgList = new ArrayList<String>(); + Set<SdInstallArgs> keys = processCids.keySet(); + boolean doGc = false; + for (SdInstallArgs args : keys) { + String codePath = processCids.get(args); + if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading container : " + + args.cid); + int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR; + try { + // Make sure there are no container errors first. + if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) + != PackageManager.INSTALL_SUCCEEDED) { + Slog.e(TAG, "Failed to mount cid : " + args.cid + + " when installing from sdcard"); + continue; + } + // Check code path here. + if (codePath == null || !codePath.equals(args.getCodePath())) { + Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()+ + " does not match one in settings " + codePath); + continue; + } + // Parse package + int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags; + doGc = true; + synchronized (mInstallLock) { + final PackageParser.Package pkg = scanPackageLI(new File(codePath), + parseFlags, 0); + // Scan the package + if (pkg != null) { + synchronized (mPackages) { + retCode = PackageManager.INSTALL_SUCCEEDED; + pkgList.add(pkg.packageName); + // Post process args + args.doPostInstall(PackageManager.INSTALL_SUCCEEDED); + } + } else { + Slog.i(TAG, "Failed to install pkg from " + + codePath + " from sdcard"); + } + } + + } finally { + if (retCode != PackageManager.INSTALL_SUCCEEDED) { + // Don't destroy container here. Wait till gc clears things up. + removeCids.add(args.cid); + } + } + } + synchronized (mPackages) { + // If the platform SDK has changed since the last time we booted, + // we need to re-grant app permission to catch any new ones that + // appear. This is really a hack, and means that apps can in some + // cases get permissions that the user didn't initially explicitly + // allow... it would be nice to have some better way to handle + // this situation. + final boolean regrantPermissions = mSettings.mExternalSdkPlatform + != mSdkVersion; + if (regrantPermissions) Slog.i(TAG, "Platform changed from " + + mSettings.mExternalSdkPlatform + " to " + mSdkVersion + + "; regranting permissions for external storage"); + mSettings.mExternalSdkPlatform = mSdkVersion; + + // Make sure group IDs have been assigned, and any permission + // changes in other apps are accounted for + updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions); + // Persist settings + mSettings.writeLP(); + } + // Send a broadcast to let everyone know we are done processing + if (pkgList.size() > 0) { + sendResourcesChangedBroadcast(true, pkgList, uidArr, null); + } + // Force gc to avoid any stale parser references that we might have. + if (doGc) { + Runtime.getRuntime().gc(); + } + // List stale containers. + if (removeCids != null) { + for (String cid : removeCids) { + Log.w(TAG, "Container " + cid + " is stale"); + } + } + } + + /* + * Utility method to unload a list of specified containers + */ + private void unloadAllContainers(Set<SdInstallArgs> cidArgs) { + // Just unmount all valid containers. + for (SdInstallArgs arg : cidArgs) { + synchronized (mInstallLock) { + arg.doPostDeleteLI(false); + } + } + } + + /* + * Unload packages mounted on external media. This involves deleting + * package data from internal structures, sending broadcasts about + * diabled packages, gc'ing to free up references, unmounting all + * secure containers corresponding to packages on external media, and + * posting a UPDATED_MEDIA_STATUS message if status has been requested. + * Please note that we always have to post this message if status has + * been requested no matter what. + */ + private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, + int uidArr[], final boolean reportStatus) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages"); + ArrayList<String> pkgList = new ArrayList<String>(); + ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>(); + final Set<SdInstallArgs> keys = processCids.keySet(); + for (SdInstallArgs args : keys) { + String cid = args.cid; + String pkgName = args.getPackageName(); + if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to unload pkg : " + pkgName); + // Delete package internally + PackageRemovedInfo outInfo = new PackageRemovedInfo(); + synchronized (mInstallLock) { + boolean res = deletePackageLI(pkgName, false, + PackageManager.DONT_DELETE_DATA, outInfo); + if (res) { + pkgList.add(pkgName); + } else { + Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName); + failedList.add(args); + } + } + } + // We have to absolutely send UPDATED_MEDIA_STATUS only + // after confirming that all the receivers processed the ordered + // broadcast when packages get disabled, force a gc to clean things up. + // and unload all the containers. + if (pkgList.size() > 0) { + sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() { + public void performReceive(Intent intent, int resultCode, String data, Bundle extras, + boolean ordered, boolean sticky) throws RemoteException { + Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, + reportStatus ? 1 : 0, 1, keys); + mHandler.sendMessage(msg); + } + }); + } else { + Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, + reportStatus ? 1 : 0, -1, keys); + mHandler.sendMessage(msg); + } + } + + public void movePackage(final String packageName, + final IPackageMoveObserver observer, final int flags) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MOVE_PACKAGE, null); + int returnCode = PackageManager.MOVE_SUCCEEDED; + int currFlags = 0; + int newFlags = 0; + synchronized (mPackages) { + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST; + } else { + // Disable moving fwd locked apps and system packages + if (pkg.applicationInfo != null && + (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + Slog.w(TAG, "Cannot move system application"); + returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE; + } else if (pkg.applicationInfo != null && + (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) { + Slog.w(TAG, "Cannot move forward locked app."); + returnCode = PackageManager.MOVE_FAILED_FORWARD_LOCKED; + } else { + // Find install location first + if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 && + (flags & PackageManager.MOVE_INTERNAL) != 0) { + Slog.w(TAG, "Ambigous flags specified for move location."); + returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION; + } else { + newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 ? + PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL; + currFlags = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ? + PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL; + if (newFlags == currFlags) { + Slog.w(TAG, "No move required. Trying to move to same location"); + returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION; + } + } + } + } + if (returnCode != PackageManager.MOVE_SUCCEEDED) { + processPendingMove(new MoveParams(null, observer, 0, packageName), returnCode); + } else { + Message msg = mHandler.obtainMessage(INIT_COPY); + InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir, + pkg.applicationInfo.publicSourceDir); + MoveParams mp = new MoveParams(srcArgs, observer, newFlags, + packageName); + msg.obj = mp; + mHandler.sendMessage(msg); + } + } + } + + private void processPendingMove(final MoveParams mp, final int currentStatus) { + // Queue up an async operation since the package deletion may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + int returnCode = currentStatus; + if (currentStatus == PackageManager.MOVE_SUCCEEDED) { + int uidArr[] = null; + ArrayList<String> pkgList = null; + synchronized (mPackages) { + PackageParser.Package pkg = mPackages.get(mp.packageName); + if (pkg == null ) { + Slog.w(TAG, " Package " + mp.packageName + + " doesn't exist. Aborting move"); + returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST; + } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) { + Slog.w(TAG, "Package " + mp.packageName + " code path changed from " + + mp.srcArgs.getCodePath() + " to " + pkg.applicationInfo.sourceDir + + " Aborting move and returning error"); + returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR; + } else { + uidArr = new int[] { pkg.applicationInfo.uid }; + pkgList = new ArrayList<String>(); + pkgList.add(mp.packageName); + } + } + if (returnCode == PackageManager.MOVE_SUCCEEDED) { + // Send resources unavailable broadcast + sendResourcesChangedBroadcast(false, pkgList, uidArr, null); + // Update package code and resource paths + synchronized (mInstallLock) { + synchronized (mPackages) { + PackageParser.Package pkg = mPackages.get(mp.packageName); + // Recheck for package again. + if (pkg == null ) { + Slog.w(TAG, " Package " + mp.packageName + + " doesn't exist. Aborting move"); + returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST; + } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) { + Slog.w(TAG, "Package " + mp.packageName + " code path changed from " + + mp.srcArgs.getCodePath() + " to " + pkg.applicationInfo.sourceDir + + " Aborting move and returning error"); + returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR; + } else { + String oldCodePath = pkg.mPath; + String newCodePath = mp.targetArgs.getCodePath(); + String newResPath = mp.targetArgs.getResourcePath(); + pkg.mPath = newCodePath; + // Move dex files around + if (moveDexFilesLI(pkg) + != PackageManager.INSTALL_SUCCEEDED) { + // Moving of dex files failed. Set + // error code and abort move. + pkg.mPath = pkg.mScanPath; + returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; + } else { + pkg.mScanPath = newCodePath; + pkg.applicationInfo.sourceDir = newCodePath; + pkg.applicationInfo.publicSourceDir = newResPath; + PackageSetting ps = (PackageSetting) pkg.mExtras; + ps.codePath = new File(pkg.applicationInfo.sourceDir); + ps.codePathString = ps.codePath.getPath(); + ps.resourcePath = new File(pkg.applicationInfo.publicSourceDir); + ps.resourcePathString = ps.resourcePath.getPath(); + // Set the application info flag correctly. + if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE; + } else { + pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE; + } + ps.setFlags(pkg.applicationInfo.flags); + mAppDirs.remove(oldCodePath); + mAppDirs.put(newCodePath, pkg); + // Persist settings + mSettings.writeLP(); + } + } + } + } + // Send resources available broadcast + sendResourcesChangedBroadcast(true, pkgList, uidArr, null); + } + } + if (returnCode != PackageManager.MOVE_SUCCEEDED){ + // Clean up failed installation + if (mp.targetArgs != null) { + mp.targetArgs.doPostInstall(PackageManager.INSTALL_FAILED_INTERNAL_ERROR); + } + } else { + // Force a gc to clear things up. + Runtime.getRuntime().gc(); + // Delete older code + synchronized (mInstallLock) { + mp.srcArgs.doPostDeleteLI(true); + } + } + IPackageMoveObserver observer = mp.observer; + if (observer != null) { + try { + observer.packageMoved(mp.packageName, returnCode); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } + } + } + }); + } + + public boolean setInstallLocation(int loc) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.WRITE_SECURE_SETTINGS, null); + if (getInstallLocation() == loc) { + return true; + } + if (loc == PackageHelper.APP_INSTALL_AUTO || + loc == PackageHelper.APP_INSTALL_INTERNAL || + loc == PackageHelper.APP_INSTALL_EXTERNAL) { + android.provider.Settings.System.putInt(mContext.getContentResolver(), + android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, loc); + return true; + } + return false; + } + + public int getInstallLocation() { + return android.provider.Settings.System.getInt(mContext.getContentResolver(), + android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, PackageHelper.APP_INSTALL_AUTO); + } } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 966ecb0..b9021b0 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -17,6 +17,7 @@ package com.android.server; import com.android.internal.app.IBatteryStats; +import com.android.internal.app.ShutdownThread; import com.android.server.am.BatteryStatsService; import android.app.ActivityManagerNative; @@ -29,6 +30,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.database.ContentObserver; import android.database.Cursor; import android.hardware.Sensor; import android.hardware.SensorEvent; @@ -36,6 +38,7 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.BatteryStats; import android.os.Binder; +import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -45,11 +48,13 @@ import android.os.Power; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.provider.Settings.SettingNotFoundException; import android.provider.Settings; import android.util.EventLog; import android.util.Log; +import android.util.Slog; import android.view.WindowManagerPolicy; import static android.provider.Settings.System.DIM_SCREEN; import static android.provider.Settings.System.SCREEN_BRIGHTNESS; @@ -59,6 +64,7 @@ import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; import static android.provider.Settings.System.STAY_ON_WHILE_PLUGGED_IN; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; @@ -84,7 +90,7 @@ class PowerManagerService extends IPowerManager.Stub | PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK; // time since last state: time since last event: - // The short keylight delay comes from Gservices; this is the default. + // The short keylight delay comes from secure settings; this is the default. private static final int SHORT_KEYLIGHT_DELAY_DEFAULT = 6000; // t+6 sec private static final int MEDIUM_KEYLIGHT_DELAY = 15000; // t+15 sec private static final int LONG_KEYLIGHT_DELAY = 6000; // t+6 sec @@ -99,7 +105,7 @@ class PowerManagerService extends IPowerManager.Stub // trigger proximity if distance is less than 5 cm private static final float PROXIMITY_THRESHOLD = 5.0f; - // Cached Gservices settings; see updateGservicesValues() + // Cached secure settings; see updateSettingsValues() private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT; // flags for setPowerState @@ -132,7 +138,7 @@ class PowerManagerService extends IPowerManager.Stub static final boolean ANIMATE_SCREEN_LIGHTS = true; static final boolean ANIMATE_BUTTON_LIGHTS = false; static final boolean ANIMATE_KEYBOARD_LIGHTS = false; - + static final int ANIM_STEPS = 60/4; // Slower animation for autobrightness changes static final int AUTOBRIGHTNESS_ANIM_STEPS = 60; @@ -143,21 +149,14 @@ class PowerManagerService extends IPowerManager.Stub static final int INITIAL_SCREEN_BRIGHTNESS = 255; static final int INITIAL_BUTTON_BRIGHTNESS = Power.BRIGHTNESS_OFF; static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF; - - static final int LOG_POWER_SLEEP_REQUESTED = 2724; - static final int LOG_POWER_SCREEN_BROADCAST_SEND = 2725; - static final int LOG_POWER_SCREEN_BROADCAST_DONE = 2726; - static final int LOG_POWER_SCREEN_BROADCAST_STOP = 2727; - static final int LOG_POWER_SCREEN_STATE = 2728; - static final int LOG_POWER_PARTIAL_WAKE_STATE = 2729; - + private final int MY_UID; private boolean mDoneBooting = false; private boolean mBootCompleted = false; private int mStayOnConditions = 0; - private int[] mBroadcastQueue = new int[] { -1, -1, -1 }; - private int[] mBroadcastWhy = new int[3]; + private final int[] mBroadcastQueue = new int[] { -1, -1, -1 }; + private final int[] mBroadcastWhy = new int[3]; private int mPartialCount = 0; private int mPowerState; // mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER, @@ -171,7 +170,8 @@ class PowerManagerService extends IPowerManager.Stub private boolean mProximitySensorActive = false; private int mProximityPendingValue = -1; // -1 == nothing, 0 == inactive, 1 == active private long mLastProximityEventTime; - private int mTotalDelaySetting; + private int mScreenOffTimeoutSetting; + private int mMaximumScreenOffTimeout = Integer.MAX_VALUE; private int mKeylightDelay; private int mDimDelay; private int mScreenOffDelay; @@ -182,8 +182,12 @@ class PowerManagerService extends IPowerManager.Stub private final LockList mLocks = new LockList(); private Intent mScreenOffIntent; private Intent mScreenOnIntent; - private HardwareService mHardware; + private LightsService mLightsService; private Context mContext; + private LightsService.Light mLcdLight; + private LightsService.Light mButtonLight; + private LightsService.Light mKeyboardLight; + private LightsService.Light mAttentionLight; private UnsynchronizedWakeLock mBroadcastWakeLock; private UnsynchronizedWakeLock mStayOnWhilePluggedInScreenDimLock; private UnsynchronizedWakeLock mStayOnWhilePluggedInPartialLock; @@ -191,8 +195,8 @@ class PowerManagerService extends IPowerManager.Stub private UnsynchronizedWakeLock mProximityPartialLock; private HandlerThread mHandlerThread; private Handler mHandler; - private TimeoutTask mTimeoutTask = new TimeoutTask(); - private LightAnimator mLightAnimator = new LightAnimator(); + private final TimeoutTask mTimeoutTask = new TimeoutTask(); + private final LightAnimator mLightAnimator = new LightAnimator(); private final BrightnessState mScreenBrightness = new BrightnessState(SCREEN_BRIGHT_BIT); private final BrightnessState mKeyboardBrightness @@ -209,22 +213,23 @@ class PowerManagerService extends IPowerManager.Stub private Sensor mLightSensor; private boolean mLightSensorEnabled; private float mLightSensorValue = -1; + private int mHighestLightSensorValue = -1; private float mLightSensorPendingValue = -1; - private int mLightSensorBrightness = -1; + private int mLightSensorScreenBrightness = -1; + private int mLightSensorButtonBrightness = -1; + private int mLightSensorKeyboardBrightness = -1; private boolean mDimScreen = true; + private boolean mIsDocked = false; private long mNextTimeout; private volatile int mPokey = 0; private volatile boolean mPokeAwakeOnSet = false; private volatile boolean mInitComplete = false; - private HashMap<IBinder,PokeLock> mPokeLocks = new HashMap<IBinder,PokeLock>(); - // mScreenOnTime and mScreenOnStartTime are used for computing total time screen - // has been on since boot - private long mScreenOnTime; - private long mScreenOnStartTime; + private final HashMap<IBinder,PokeLock> mPokeLocks = new HashMap<IBinder,PokeLock>(); // mLastScreenOnTime is the time the screen was last turned on private long mLastScreenOnTime; private boolean mPreventScreenOn; private int mScreenBrightnessOverride = -1; + private int mButtonBrightnessOverride = -1; private boolean mUseSoftwareAutoBrightness; private boolean mAutoBrightessEnabled; private int[] mAutoBrightnessLevels; @@ -250,25 +255,25 @@ class PowerManagerService extends IPowerManager.Stub mLog = new PrintStream("/data/power.log"); } catch (FileNotFoundException e) { - android.util.Log.e(TAG, "Life is hard", e); + android.util.Slog.e(TAG, "Life is hard", e); } } static class Log { static void d(String tag, String s) { mLog.println(s); - android.util.Log.d(tag, s); + android.util.Slog.d(tag, s); } static void i(String tag, String s) { mLog.println(s); - android.util.Log.i(tag, s); + android.util.Slog.i(tag, s); } static void w(String tag, String s) { mLog.println(s); - android.util.Log.w(tag, s); + android.util.Slog.w(tag, s); } static void e(String tag, String s) { mLog.println(s); - android.util.Log.e(tag, s); + android.util.Slog.e(tag, s); } } */ @@ -344,10 +349,14 @@ class PowerManagerService extends IPowerManager.Stub // treat plugging and unplugging the devices as a user activity. // users find it disconcerting when they unplug the device // and it shuts off right away. + // to avoid turning on the screen when unplugging, we only trigger + // user activity when screen was already on. // temporarily set mUserActivityAllowed to true so this will work // even when the keyguard is on. synchronized (mLocks) { - forceUserActivityLocked(); + if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0) { + forceUserActivityLocked(); + } } } } @@ -361,6 +370,15 @@ class PowerManagerService extends IPowerManager.Stub } } + private final class DockReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + dockStateChanged(state); + } + } + /** * Set the setting that determines whether the device stays on when plugged in. * The argument is a bit string, with each bit specifying a power source that, @@ -377,6 +395,16 @@ class PowerManagerService extends IPowerManager.Stub Settings.System.STAY_ON_WHILE_PLUGGED_IN, val); } + public void setMaximumScreenOffTimeount(int timeMs) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.WRITE_SECURE_SETTINGS, null); + synchronized (mLocks) { + mMaximumScreenOffTimeout = timeMs; + // recalculate everything + setScreenOffTimeoutsLocked(); + } + } + private class SettingsObserver implements Observer { private int getInt(String name) { return mSettings.getValues(name).getAsInteger(Settings.System.VALUE); @@ -389,7 +417,7 @@ class PowerManagerService extends IPowerManager.Stub updateWakeLockLocked(); // SCREEN_OFF_TIMEOUT - mTotalDelaySetting = getInt(SCREEN_OFF_TIMEOUT); + mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT); // DIM_SCREEN //mDimScreen = getInt(DIM_SCREEN) != 0; @@ -415,22 +443,26 @@ class PowerManagerService extends IPowerManager.Stub // assume nothing is on yet mUserState = mPowerState = 0; - + // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); - mScreenOnStartTime = SystemClock.elapsedRealtime(); } private ContentQueryMap mSettings; - void init(Context context, HardwareService hardware, IActivityManager activity, + void init(Context context, LightsService lights, IActivityManager activity, BatteryService battery) { - mHardware = hardware; + mLightsService = lights; mContext = context; mActivityService = activity; mBatteryStats = BatteryStatsService.getService(); mBatteryService = battery; + mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT); + mButtonLight = lights.getLight(LightsService.LIGHT_ID_BUTTONS); + mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD); + mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); + mHandlerThread = new HandlerThread("PowerManagerService") { @Override protected void onLooperPrepared() { @@ -439,7 +471,7 @@ class PowerManagerService extends IPowerManager.Stub } }; mHandlerThread.start(); - + synchronized (mHandlerThread) { while (!mInitComplete) { try { @@ -450,7 +482,7 @@ class PowerManagerService extends IPowerManager.Stub } } } - + void initInThread() { mHandler = new Handler(); @@ -511,21 +543,19 @@ class PowerManagerService extends IPowerManager.Stub filter = new IntentFilter(); filter.addAction(Intent.ACTION_BOOT_COMPLETED); mContext.registerReceiver(new BootCompletedReceiver(), filter); - - // Listen for Gservices changes - IntentFilter gservicesChangedFilter = - new IntentFilter(Settings.Gservices.CHANGED_ACTION); - mContext.registerReceiver(new GservicesChangedReceiver(), gservicesChangedFilter); - // And explicitly do the initial update of our cached settings - updateGservicesValues(); - - if (mUseSoftwareAutoBrightness) { - // turn the screen on - setPowerState(SCREEN_BRIGHT); - } else { - // turn everything on - setPowerState(ALL_BRIGHT); - } + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_DOCK_EVENT); + mContext.registerReceiver(new DockReceiver(), filter); + + // Listen for secure settings changes + mContext.getContentResolver().registerContentObserver( + Settings.Secure.CONTENT_URI, true, + new ContentObserver(new Handler()) { + public void onChange(boolean selfChange) { + updateSettingsValues(); + } + }); + updateSettingsValues(); synchronized (mHandlerThread) { mInitComplete = true; @@ -541,6 +571,7 @@ class PowerManagerService extends IPowerManager.Stub binder = b; tag = t; uid = u == MY_UID ? Process.SYSTEM_UID : u; + pid = Binder.getCallingPid(); if (u != MY_UID || ( !"KEEP_SCREEN_ON_FLAG".equals(tag) && !"KeyInputQueue".equals(tag))) { @@ -565,6 +596,7 @@ class PowerManagerService extends IPowerManager.Stub final IBinder binder; final String tag; final int uid; + final int pid; final int monitorType; boolean activated = true; int minState; @@ -610,7 +642,7 @@ class PowerManagerService extends IPowerManager.Stub int acquireType = -1; if (mSpew) { - Log.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag); + Slog.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag); } int index = mLocks.getIndex(lock); @@ -639,7 +671,7 @@ class PowerManagerService extends IPowerManager.Stub default: // just log and bail. we're in the server, so don't // throw an exception. - Log.e(TAG, "bad wakelock type for lock '" + tag + "' " + Slog.e(TAG, "bad wakelock type for lock '" + tag + "' " + " flags=" + flags); return; } @@ -658,14 +690,14 @@ class PowerManagerService extends IPowerManager.Stub int oldWakeLockState = mWakeLockState; mWakeLockState = mLocks.reactivateScreenLocksLocked(); if (mSpew) { - Log.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState) + Slog.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState) + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState) + " previous wakeLockState=0x" + Integer.toHexString(oldWakeLockState)); } } else { if (mSpew) { - Log.d(TAG, "here mUserState=0x" + Integer.toHexString(mUserState) + Slog.d(TAG, "here mUserState=0x" + Integer.toHexString(mUserState) + " mLocks.gatherState()=0x" + Integer.toHexString(mLocks.gatherState()) + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)); @@ -678,7 +710,7 @@ class PowerManagerService extends IPowerManager.Stub if (newlock) { mPartialCount++; if (mPartialCount == 1) { - if (LOG_PARTIAL_WL) EventLog.writeEvent(LOG_POWER_PARTIAL_WAKE_STATE, 1, tag); + if (LOG_PARTIAL_WL) EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 1, tag); } } Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME); @@ -723,9 +755,9 @@ class PowerManagerService extends IPowerManager.Stub if (wl == null) { return; } - + if (mSpew) { - Log.d(TAG, "releaseWakeLock flags=0x" + Slog.d(TAG, "releaseWakeLock flags=0x" + Integer.toHexString(wl.flags) + " tag=" + wl.tag); } @@ -740,7 +772,7 @@ class PowerManagerService extends IPowerManager.Stub else if ((wl.flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) { mPartialCount--; if (mPartialCount == 0) { - if (LOG_PARTIAL_WL) EventLog.writeEvent(LOG_POWER_PARTIAL_WAKE_STATE, 0, wl.tag); + if (LOG_PARTIAL_WL) EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 0, wl.tag); Power.releaseWakeLock(PARTIAL_NAME); } } else if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) { @@ -750,7 +782,7 @@ class PowerManagerService extends IPowerManager.Stub ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0)) { // wait for proximity sensor to go negative before disabling sensor if (mDebugProximitySensor) { - Log.d(TAG, "waiting for proximity sensor to go negative"); + Slog.d(TAG, "waiting for proximity sensor to go negative"); } } else { disableProximityLockLocked(); @@ -800,7 +832,7 @@ class PowerManagerService extends IPowerManager.Stub public void setPokeLock(int pokey, IBinder token, String tag) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); if (token == null) { - Log.e(TAG, "setPokeLock got null token for tag='" + tag + "'"); + Slog.e(TAG, "setPokeLock got null token for tag='" + tag + "'"); return; } @@ -847,7 +879,7 @@ class PowerManagerService extends IPowerManager.Stub int oldCumulativeTimeout = oldPokey & POKE_LOCK_TIMEOUT_MASK; int newCumulativeTimeout = pokey & POKE_LOCK_TIMEOUT_MASK; - + if (oldCumulativeTimeout != newCumulativeTimeout) { setScreenOffTimeoutsLocked(); // reset the countdown timer, but use the existing nextState so it doesn't @@ -899,87 +931,95 @@ class PowerManagerService extends IPowerManager.Stub long now = SystemClock.uptimeMillis(); - pw.println("Power Manager State:"); - pw.println(" mIsPowered=" + mIsPowered - + " mPowerState=" + mPowerState - + " mScreenOffTime=" + (SystemClock.elapsedRealtime()-mScreenOffTime) - + " ms"); - pw.println(" mPartialCount=" + mPartialCount); - pw.println(" mWakeLockState=" + dumpPowerState(mWakeLockState)); - pw.println(" mUserState=" + dumpPowerState(mUserState)); - pw.println(" mPowerState=" + dumpPowerState(mPowerState)); - pw.println(" mLocks.gather=" + dumpPowerState(mLocks.gatherState())); - pw.println(" mNextTimeout=" + mNextTimeout + " now=" + now - + " " + ((mNextTimeout-now)/1000) + "s from now"); - pw.println(" mDimScreen=" + mDimScreen - + " mStayOnConditions=" + mStayOnConditions); - pw.println(" mScreenOffReason=" + mScreenOffReason - + " mUserState=" + mUserState); - pw.println(" mBroadcastQueue={" + mBroadcastQueue[0] + ',' + mBroadcastQueue[1] - + ',' + mBroadcastQueue[2] + "}"); - pw.println(" mBroadcastWhy={" + mBroadcastWhy[0] + ',' + mBroadcastWhy[1] - + ',' + mBroadcastWhy[2] + "}"); - pw.println(" mPokey=" + mPokey + " mPokeAwakeonSet=" + mPokeAwakeOnSet); - pw.println(" mKeyboardVisible=" + mKeyboardVisible - + " mUserActivityAllowed=" + mUserActivityAllowed); - pw.println(" mKeylightDelay=" + mKeylightDelay + " mDimDelay=" + mDimDelay - + " mScreenOffDelay=" + mScreenOffDelay); - pw.println(" mPreventScreenOn=" + mPreventScreenOn - + " mScreenBrightnessOverride=" + mScreenBrightnessOverride); - pw.println(" mTotalDelaySetting=" + mTotalDelaySetting); - pw.println(" mLastScreenOnTime=" + mLastScreenOnTime); - pw.println(" mBroadcastWakeLock=" + mBroadcastWakeLock); - pw.println(" mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock); - pw.println(" mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock); - pw.println(" mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock); - pw.println(" mProximityPartialLock=" + mProximityPartialLock); - pw.println(" mProximityWakeLockCount=" + mProximityWakeLockCount); - pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled); - pw.println(" mProximitySensorActive=" + mProximitySensorActive); - pw.println(" mProximityPendingValue=" + mProximityPendingValue); - pw.println(" mLastProximityEventTime=" + mLastProximityEventTime); - pw.println(" mLightSensorEnabled=" + mLightSensorEnabled); - pw.println(" mLightSensorValue=" + mLightSensorValue); - pw.println(" mLightSensorPendingValue=" + mLightSensorPendingValue); - pw.println(" mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness); - pw.println(" mAutoBrightessEnabled=" + mAutoBrightessEnabled); - mScreenBrightness.dump(pw, " mScreenBrightness: "); - mKeyboardBrightness.dump(pw, " mKeyboardBrightness: "); - mButtonBrightness.dump(pw, " mButtonBrightness: "); - - int N = mLocks.size(); - pw.println(); - pw.println("mLocks.size=" + N + ":"); - for (int i=0; i<N; i++) { - WakeLock wl = mLocks.get(i); - String type = lockType(wl.flags & LOCK_MASK); - String acquireCausesWakeup = ""; - if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { - acquireCausesWakeup = "ACQUIRE_CAUSES_WAKEUP "; + synchronized (mLocks) { + pw.println("Power Manager State:"); + pw.println(" mIsPowered=" + mIsPowered + + " mPowerState=" + mPowerState + + " mScreenOffTime=" + (SystemClock.elapsedRealtime()-mScreenOffTime) + + " ms"); + pw.println(" mPartialCount=" + mPartialCount); + pw.println(" mWakeLockState=" + dumpPowerState(mWakeLockState)); + pw.println(" mUserState=" + dumpPowerState(mUserState)); + pw.println(" mPowerState=" + dumpPowerState(mPowerState)); + pw.println(" mLocks.gather=" + dumpPowerState(mLocks.gatherState())); + pw.println(" mNextTimeout=" + mNextTimeout + " now=" + now + + " " + ((mNextTimeout-now)/1000) + "s from now"); + pw.println(" mDimScreen=" + mDimScreen + + " mStayOnConditions=" + mStayOnConditions); + pw.println(" mScreenOffReason=" + mScreenOffReason + + " mUserState=" + mUserState); + pw.println(" mBroadcastQueue={" + mBroadcastQueue[0] + ',' + mBroadcastQueue[1] + + ',' + mBroadcastQueue[2] + "}"); + pw.println(" mBroadcastWhy={" + mBroadcastWhy[0] + ',' + mBroadcastWhy[1] + + ',' + mBroadcastWhy[2] + "}"); + pw.println(" mPokey=" + mPokey + " mPokeAwakeonSet=" + mPokeAwakeOnSet); + pw.println(" mKeyboardVisible=" + mKeyboardVisible + + " mUserActivityAllowed=" + mUserActivityAllowed); + pw.println(" mKeylightDelay=" + mKeylightDelay + " mDimDelay=" + mDimDelay + + " mScreenOffDelay=" + mScreenOffDelay); + pw.println(" mPreventScreenOn=" + mPreventScreenOn + + " mScreenBrightnessOverride=" + mScreenBrightnessOverride + + " mButtonBrightnessOverride=" + mButtonBrightnessOverride); + pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting + + " mMaximumScreenOffTimeout=" + mMaximumScreenOffTimeout); + pw.println(" mLastScreenOnTime=" + mLastScreenOnTime); + pw.println(" mBroadcastWakeLock=" + mBroadcastWakeLock); + pw.println(" mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock); + pw.println(" mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock); + pw.println(" mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock); + pw.println(" mProximityPartialLock=" + mProximityPartialLock); + pw.println(" mProximityWakeLockCount=" + mProximityWakeLockCount); + pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled); + pw.println(" mProximitySensorActive=" + mProximitySensorActive); + pw.println(" mProximityPendingValue=" + mProximityPendingValue); + pw.println(" mLastProximityEventTime=" + mLastProximityEventTime); + pw.println(" mLightSensorEnabled=" + mLightSensorEnabled); + pw.println(" mLightSensorValue=" + mLightSensorValue + + " mLightSensorPendingValue=" + mLightSensorPendingValue); + pw.println(" mLightSensorScreenBrightness=" + mLightSensorScreenBrightness + + " mLightSensorButtonBrightness=" + mLightSensorButtonBrightness + + " mLightSensorKeyboardBrightness=" + mLightSensorKeyboardBrightness); + pw.println(" mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness); + pw.println(" mAutoBrightessEnabled=" + mAutoBrightessEnabled); + mScreenBrightness.dump(pw, " mScreenBrightness: "); + mKeyboardBrightness.dump(pw, " mKeyboardBrightness: "); + mButtonBrightness.dump(pw, " mButtonBrightness: "); + + int N = mLocks.size(); + pw.println(); + pw.println("mLocks.size=" + N + ":"); + for (int i=0; i<N; i++) { + WakeLock wl = mLocks.get(i); + String type = lockType(wl.flags & LOCK_MASK); + String acquireCausesWakeup = ""; + if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { + acquireCausesWakeup = "ACQUIRE_CAUSES_WAKEUP "; + } + String activated = ""; + if (wl.activated) { + activated = " activated"; + } + pw.println(" " + type + " '" + wl.tag + "'" + acquireCausesWakeup + + activated + " (minState=" + wl.minState + ", uid=" + wl.uid + + ", pid=" + wl.pid + ")"); } - String activated = ""; - if (wl.activated) { - activated = " activated"; + + pw.println(); + pw.println("mPokeLocks.size=" + mPokeLocks.size() + ":"); + for (PokeLock p: mPokeLocks.values()) { + pw.println(" poke lock '" + p.tag + "':" + + ((p.pokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0 + ? " POKE_LOCK_IGNORE_CHEEK_EVENTS" : "") + + ((p.pokey & POKE_LOCK_IGNORE_TOUCH_AND_CHEEK_EVENTS) != 0 + ? " POKE_LOCK_IGNORE_TOUCH_AND_CHEEK_EVENTS" : "") + + ((p.pokey & POKE_LOCK_SHORT_TIMEOUT) != 0 + ? " POKE_LOCK_SHORT_TIMEOUT" : "") + + ((p.pokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0 + ? " POKE_LOCK_MEDIUM_TIMEOUT" : "")); } - pw.println(" " + type + " '" + wl.tag + "'" + acquireCausesWakeup - + activated + " (minState=" + wl.minState + ")"); - } - - pw.println(); - pw.println("mPokeLocks.size=" + mPokeLocks.size() + ":"); - for (PokeLock p: mPokeLocks.values()) { - pw.println(" poke lock '" + p.tag + "':" - + ((p.pokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0 - ? " POKE_LOCK_IGNORE_CHEEK_EVENTS" : "") - + ((p.pokey & POKE_LOCK_IGNORE_TOUCH_AND_CHEEK_EVENTS) != 0 - ? " POKE_LOCK_IGNORE_TOUCH_AND_CHEEK_EVENTS" : "") - + ((p.pokey & POKE_LOCK_SHORT_TIMEOUT) != 0 - ? " POKE_LOCK_SHORT_TIMEOUT" : "") - + ((p.pokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0 - ? " POKE_LOCK_MEDIUM_TIMEOUT" : "")); - } - pw.println(); + pw.println(); + } } private void setTimeoutLocked(long now, int nextState) @@ -998,7 +1038,7 @@ class PowerManagerService extends IPowerManager.Stub when += mDimDelay; break; } else { - Log.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim"); + Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim"); } case SCREEN_OFF: synchronized (mLocks) { @@ -1007,7 +1047,7 @@ class PowerManagerService extends IPowerManager.Stub break; } if (mSpew) { - Log.d(TAG, "setTimeoutLocked now=" + now + " nextState=" + nextState + Slog.d(TAG, "setTimeoutLocked now=" + now + " nextState=" + nextState + " when=" + when); } mHandler.postAtTime(mTimeoutTask, when); @@ -1028,7 +1068,7 @@ class PowerManagerService extends IPowerManager.Stub { synchronized (mLocks) { if (mSpew) { - Log.d(TAG, "user activity timeout timed out nextState=" + this.nextState); + Slog.d(TAG, "user activity timeout timed out nextState=" + this.nextState); } if (nextState == -1) { @@ -1077,15 +1117,16 @@ class PowerManagerService extends IPowerManager.Stub // 0 was to turn it off, and we can't strip that, because keyguard needs to come // on, so have to run the queue then. if (index == 2) { - // Also, while we're collapsing them, if it's going to be an "off," and one - // is off because of user, then use that, regardless of whether it's the first - // or second one. - if (!on && why == WindowManagerPolicy.OFF_BECAUSE_OF_USER) { - mBroadcastWhy[0] = WindowManagerPolicy.OFF_BECAUSE_OF_USER; + // While we're collapsing them, if it's going off, and the new reason + // is more significant than the first, then use the new one. + if (!on && mBroadcastWhy[0] > why) { + mBroadcastWhy[0] = why; } mBroadcastQueue[0] = on ? 1 : 0; mBroadcastQueue[1] = -1; mBroadcastQueue[2] = -1; + mBroadcastWakeLock.release(); + mBroadcastWakeLock.release(); index = 0; } if (index == 1 && !on) { @@ -1094,7 +1135,7 @@ class PowerManagerService extends IPowerManager.Stub index = -1; // The wake lock was being held, but we're not actually going to do any // broadcasts, so release the wake lock. - EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount); + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount); mBroadcastWakeLock.release(); } @@ -1105,7 +1146,7 @@ class PowerManagerService extends IPowerManager.Stub // We always increment the ref count for each notification in the queue // and always decrement when that notification is handled. mBroadcastWakeLock.acquire(); - EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_SEND, mBroadcastWakeLock.mCount); + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, mBroadcastWakeLock.mCount); mHandler.post(mNotificationTask); } } @@ -1129,7 +1170,7 @@ class PowerManagerService extends IPowerManager.Stub } if (value == 1) { mScreenOnStart = SystemClock.uptimeMillis(); - + policy.screenTurnedOn(); try { ActivityManagerNative.getDefault().wakingUp(); @@ -1138,14 +1179,14 @@ class PowerManagerService extends IPowerManager.Stub } if (mSpew) { - Log.d(TAG, "mBroadcastWakeLock=" + mBroadcastWakeLock); + Slog.d(TAG, "mBroadcastWakeLock=" + mBroadcastWakeLock); } if (mContext != null && ActivityManagerNative.isSystemReady()) { mContext.sendOrderedBroadcast(mScreenOnIntent, null, mScreenOnBroadcastDone, mHandler, 0, null, null); } else { synchronized (mLocks) { - EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_STOP, 2, + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, mBroadcastWakeLock.mCount); mBroadcastWakeLock.release(); } @@ -1153,7 +1194,7 @@ class PowerManagerService extends IPowerManager.Stub } else if (value == 0) { mScreenOffStart = SystemClock.uptimeMillis(); - + policy.screenTurnedOff(why); try { ActivityManagerNative.getDefault().goingToSleep(); @@ -1166,7 +1207,7 @@ class PowerManagerService extends IPowerManager.Stub mScreenOffBroadcastDone, mHandler, 0, null, null); } else { synchronized (mLocks) { - EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_STOP, 3, + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, mBroadcastWakeLock.mCount); mBroadcastWakeLock.release(); } @@ -1185,7 +1226,7 @@ class PowerManagerService extends IPowerManager.Stub private BroadcastReceiver mScreenOnBroadcastDone = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { synchronized (mLocks) { - EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_DONE, 1, + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1, SystemClock.uptimeMillis() - mScreenOnStart, mBroadcastWakeLock.mCount); mBroadcastWakeLock.release(); } @@ -1196,7 +1237,7 @@ class PowerManagerService extends IPowerManager.Stub private BroadcastReceiver mScreenOffBroadcastDone = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { synchronized (mLocks) { - EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_DONE, 0, + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0, SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount); mBroadcastWakeLock.release(); } @@ -1282,12 +1323,12 @@ class PowerManagerService extends IPowerManager.Stub // a prior preventScreenOn(true) call.) if (!mProximitySensorActive && (mPowerState & SCREEN_ON_BIT) != 0) { if (mSpew) { - Log.d(TAG, + Slog.d(TAG, "preventScreenOn: turning on after a prior preventScreenOn(true)!"); } int err = setScreenStateLocked(true); if (err != 0) { - Log.w(TAG, "preventScreenOn: error from setScreenStateLocked(): " + err); + Slog.w(TAG, "preventScreenOn: error from setScreenStateLocked(): " + err); } } @@ -1308,7 +1349,18 @@ class PowerManagerService extends IPowerManager.Stub } } } - + + public void setButtonBrightnessOverride(int brightness) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + synchronized (mLocks) { + if (mButtonBrightnessOverride != brightness) { + mButtonBrightnessOverride = brightness; + updateLightsLocked(mPowerState, BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT); + } + } + } + /** * Sanity-check that gets called 5 seconds after any call to * preventScreenOn(true). This ensures that the original call @@ -1319,7 +1371,7 @@ class PowerManagerService extends IPowerManager.Stub // we should have already removed any existing // mForceReenableScreenTask messages... if (!mPreventScreenOn) { - Log.w(TAG, "forceReenableScreen: mPreventScreenOn is false, nothing to do"); + Slog.w(TAG, "forceReenableScreen: mPreventScreenOn is false, nothing to do"); return; } @@ -1331,7 +1383,7 @@ class PowerManagerService extends IPowerManager.Stub // crashed before doing so.) // Log a warning, and forcibly turn the screen back on. - Log.w(TAG, "App called preventScreenOn(true) but didn't promptly reenable the screen! " + Slog.w(TAG, "App called preventScreenOn(true) but didn't promptly reenable the screen! " + "Forcing the screen back on..."); preventScreenOn(false); } @@ -1350,16 +1402,13 @@ class PowerManagerService extends IPowerManager.Stub enableLightSensor(on); if (!on) { // make sure button and key backlights are off too - int brightnessMode = (mUseSoftwareAutoBrightness - ? HardwareService.BRIGHTNESS_MODE_SENSOR - : HardwareService.BRIGHTNESS_MODE_USER); - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, 0, - brightnessMode); - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, 0, - brightnessMode); + mButtonLight.turnOff(); + mKeyboardLight.turnOff(); // clear current value so we will update based on the new conditions // when the sensor is reenabled. mLightSensorValue = -1; + // reset our highest light sensor value when the screen turns off + mHighestLightSensorValue = -1; } } } @@ -1377,7 +1426,7 @@ class PowerManagerService extends IPowerManager.Stub int err; if (mSpew) { - Log.d(TAG, "setPowerState: mPowerState=0x" + Integer.toHexString(mPowerState) + Slog.d(TAG, "setPowerState: mPowerState=0x" + Integer.toHexString(mPowerState) + " newState=0x" + Integer.toHexString(newState) + " noChangeLights=" + noChangeLights + " reason=" + reason); @@ -1408,17 +1457,17 @@ class PowerManagerService extends IPowerManager.Stub boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0; if (mSpew) { - Log.d(TAG, "setPowerState: mPowerState=" + mPowerState + Slog.d(TAG, "setPowerState: mPowerState=" + mPowerState + " newState=" + newState + " noChangeLights=" + noChangeLights); - Log.d(TAG, " oldKeyboardBright=" + ((mPowerState & KEYBOARD_BRIGHT_BIT) != 0) + Slog.d(TAG, " oldKeyboardBright=" + ((mPowerState & KEYBOARD_BRIGHT_BIT) != 0) + " newKeyboardBright=" + ((newState & KEYBOARD_BRIGHT_BIT) != 0)); - Log.d(TAG, " oldScreenBright=" + ((mPowerState & SCREEN_BRIGHT_BIT) != 0) + Slog.d(TAG, " oldScreenBright=" + ((mPowerState & SCREEN_BRIGHT_BIT) != 0) + " newScreenBright=" + ((newState & SCREEN_BRIGHT_BIT) != 0)); - Log.d(TAG, " oldButtonBright=" + ((mPowerState & BUTTON_BRIGHT_BIT) != 0) + Slog.d(TAG, " oldButtonBright=" + ((mPowerState & BUTTON_BRIGHT_BIT) != 0) + " newButtonBright=" + ((newState & BUTTON_BRIGHT_BIT) != 0)); - Log.d(TAG, " oldScreenOn=" + oldScreenOn + Slog.d(TAG, " oldScreenOn=" + oldScreenOn + " newScreenOn=" + newScreenOn); - Log.d(TAG, " oldBatteryLow=" + ((mPowerState & BATTERY_LOW_BIT) != 0) + Slog.d(TAG, " oldBatteryLow=" + ((mPowerState & BATTERY_LOW_BIT) != 0) + " newBatteryLow=" + ((newState & BATTERY_LOW_BIT) != 0)); } @@ -1444,13 +1493,13 @@ class PowerManagerService extends IPowerManager.Stub // screen forever; see forceReenableScreen().) boolean reallyTurnScreenOn = true; if (mSpew) { - Log.d(TAG, "- turning screen on... mPreventScreenOn = " + Slog.d(TAG, "- turning screen on... mPreventScreenOn = " + mPreventScreenOn); } if (mPreventScreenOn) { if (mSpew) { - Log.d(TAG, "- PREVENTING screen from really turning on!"); + Slog.d(TAG, "- PREVENTING screen from really turning on!"); } reallyTurnScreenOn = false; } @@ -1458,11 +1507,10 @@ class PowerManagerService extends IPowerManager.Stub err = setScreenStateLocked(true); long identity = Binder.clearCallingIdentity(); try { - mBatteryStats.noteScreenBrightness( - getPreferredBrightness()); + mBatteryStats.noteScreenBrightness(getPreferredBrightness()); mBatteryStats.noteScreenOn(); } catch (RemoteException e) { - Log.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e); + Slog.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e); } finally { Binder.restoreCallingIdentity(identity); } @@ -1472,11 +1520,10 @@ class PowerManagerService extends IPowerManager.Stub err = 0; } - mScreenOnStartTime = SystemClock.elapsedRealtime(); mLastTouchDown = 0; mTotalTouchDownTime = 0; mTouchCycles = 0; - EventLog.writeEvent(LOG_POWER_SCREEN_STATE, 1, reason, + EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, reason, mTotalTouchDownTime, mTouchCycles); if (err == 0) { mPowerState |= SCREEN_ON_BIT; @@ -1490,7 +1537,7 @@ class PowerManagerService extends IPowerManager.Stub try { mBatteryStats.noteScreenOff(); } catch (RemoteException e) { - Log.w(TAG, "RemoteException calling noteScreenOff on BatteryStatsService", e); + Slog.w(TAG, "RemoteException calling noteScreenOff on BatteryStatsService", e); } finally { Binder.restoreCallingIdentity(identity); } @@ -1506,18 +1553,14 @@ class PowerManagerService extends IPowerManager.Stub } } } - + private int screenOffFinishedAnimatingLocked(int reason) { // I don't think we need to check the current state here because all of these - // Power.setScreenState and sendNotificationLocked can both handle being + // Power.setScreenState and sendNotificationLocked can both handle being // called multiple times in the same state. -joeo - EventLog.writeEvent(LOG_POWER_SCREEN_STATE, 0, reason, mTotalTouchDownTime, mTouchCycles); + EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, reason, mTotalTouchDownTime, mTouchCycles); mLastTouchDown = 0; int err = setScreenStateLocked(false); - if (mScreenOnStartTime != 0) { - mScreenOnTime += SystemClock.elapsedRealtime() - mScreenOnStartTime; - mScreenOnStartTime = 0; - } if (err == 0) { mScreenOffReason = reason; sendNotificationLocked(false, reason); @@ -1532,27 +1575,29 @@ class PowerManagerService extends IPowerManager.Stub private void updateLightsLocked(int newState, int forceState) { final int oldState = mPowerState; + newState = applyButtonState(newState); + newState = applyKeyboardState(newState); final int realDifference = (newState ^ oldState); final int difference = realDifference | forceState; if (difference == 0) { return; } - + int offMask = 0; int dimMask = 0; int onMask = 0; int preferredBrightness = getPreferredBrightness(); boolean startAnimation = false; - + if ((difference & KEYBOARD_BRIGHT_BIT) != 0) { if (ANIMATE_KEYBOARD_LIGHTS) { if ((newState & KEYBOARD_BRIGHT_BIT) == 0) { mKeyboardBrightness.setTargetLocked(Power.BRIGHTNESS_OFF, ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS, - preferredBrightness); + Power.BRIGHTNESS_ON); } else { - mKeyboardBrightness.setTargetLocked(preferredBrightness, + mKeyboardBrightness.setTargetLocked(Power.BRIGHTNESS_ON, ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS, Power.BRIGHTNESS_OFF); } @@ -1571,9 +1616,9 @@ class PowerManagerService extends IPowerManager.Stub if ((newState & BUTTON_BRIGHT_BIT) == 0) { mButtonBrightness.setTargetLocked(Power.BRIGHTNESS_OFF, ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS, - preferredBrightness); + Power.BRIGHTNESS_ON); } else { - mButtonBrightness.setTargetLocked(preferredBrightness, + mButtonBrightness.setTargetLocked(Power.BRIGHTNESS_ON, ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS, Power.BRIGHTNESS_OFF); } @@ -1678,14 +1723,14 @@ class PowerManagerService extends IPowerManager.Stub if (startAnimation) { if (mSpew) { - Log.i(TAG, "Scheduling light animator!"); + Slog.i(TAG, "Scheduling light animator!"); } mHandler.removeCallbacks(mLightAnimator); mHandler.post(mLightAnimator); } - + if (offMask != 0) { - //Log.i(TAG, "Setting brightess off: " + offMask); + if (mSpew) Slog.i(TAG, "Setting brightess off: " + offMask); setLightBrightness(offMask, Power.BRIGHTNESS_OFF); } if (dimMask != 0) { @@ -1694,7 +1739,7 @@ class PowerManagerService extends IPowerManager.Stub brightness > Power.BRIGHTNESS_LOW_BATTERY) { brightness = Power.BRIGHTNESS_LOW_BATTERY; } - //Log.i(TAG, "Setting brightess dim " + brightness + ": " + offMask); + if (mSpew) Slog.i(TAG, "Setting brightess dim " + brightness + ": " + dimMask); setLightBrightness(dimMask, brightness); } if (onMask != 0) { @@ -1703,52 +1748,46 @@ class PowerManagerService extends IPowerManager.Stub brightness > Power.BRIGHTNESS_LOW_BATTERY) { brightness = Power.BRIGHTNESS_LOW_BATTERY; } - //Log.i(TAG, "Setting brightess on " + brightness + ": " + onMask); + if (mSpew) Slog.i(TAG, "Setting brightess on " + brightness + ": " + onMask); setLightBrightness(onMask, brightness); } } private void setLightBrightness(int mask, int value) { int brightnessMode = (mAutoBrightessEnabled - ? HardwareService.BRIGHTNESS_MODE_SENSOR - : HardwareService.BRIGHTNESS_MODE_USER); + ? LightsService.BRIGHTNESS_MODE_SENSOR + : LightsService.BRIGHTNESS_MODE_USER); if ((mask & SCREEN_BRIGHT_BIT) != 0) { - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, value, - brightnessMode); + mLcdLight.setBrightness(value, brightnessMode); } - brightnessMode = (mUseSoftwareAutoBrightness - ? HardwareService.BRIGHTNESS_MODE_SENSOR - : HardwareService.BRIGHTNESS_MODE_USER); if ((mask & BUTTON_BRIGHT_BIT) != 0) { - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, value, - brightnessMode); + mButtonLight.setBrightness(value); } if ((mask & KEYBOARD_BRIGHT_BIT) != 0) { - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, value, - brightnessMode); + mKeyboardLight.setBrightness(value); } } class BrightnessState { final int mask; - + boolean initialized; int targetValue; float curValue; float delta; boolean animating; - + BrightnessState(int m) { mask = m; } - + public void dump(PrintWriter pw, String prefix) { pw.println(prefix + "animating=" + animating + " targetValue=" + targetValue + " curValue=" + curValue + " delta=" + delta); } - + boolean setTargetLocked(int target, int stepsToTarget, int initialValue, int nominalCurrentValue) { if (!initialized) { @@ -1763,7 +1802,7 @@ class PowerManagerService extends IPowerManager.Stub / stepsToTarget; if (mSpew) { String noticeMe = nominalCurrentValue == curValue ? "" : " ******************"; - Log.i(TAG, "Setting target " + mask + ": cur=" + curValue + Slog.i(TAG, "Setting target " + mask + ": cur=" + curValue + " target=" + targetValue + " delta=" + delta + " nominalCurrentValue=" + nominalCurrentValue + noticeMe); @@ -1771,11 +1810,11 @@ class PowerManagerService extends IPowerManager.Stub animating = true; return true; } - + boolean stepLocked() { if (!animating) return false; if (false && mSpew) { - Log.i(TAG, "Step target " + mask + ": cur=" + curValue + Slog.i(TAG, "Step target " + mask + ": cur=" + curValue + " target=" + targetValue + " delta=" + delta); } curValue += delta; @@ -1795,7 +1834,7 @@ class PowerManagerService extends IPowerManager.Stub more = false; } } - //Log.i(TAG, "Animating brightess " + curIntValue + ": " + mask); + //Slog.i(TAG, "Animating brightess " + curIntValue + ": " + mask); setLightBrightness(mask, curIntValue); animating = more; if (!more) { @@ -1806,7 +1845,7 @@ class PowerManagerService extends IPowerManager.Stub return more; } } - + private class LightAnimator implements Runnable { public void run() { synchronized (mLocks) { @@ -1824,14 +1863,14 @@ class PowerManagerService extends IPowerManager.Stub } } } - + private int getPreferredBrightness() { try { if (mScreenBrightnessOverride >= 0) { return mScreenBrightnessOverride; - } else if (mLightSensorBrightness >= 0 && mUseSoftwareAutoBrightness + } else if (mLightSensorScreenBrightness >= 0 && mUseSoftwareAutoBrightness && mAutoBrightessEnabled) { - return mLightSensorBrightness; + return mLightSensorScreenBrightness; } final int brightness = Settings.System.getInt(mContext.getContentResolver(), SCREEN_BRIGHTNESS); @@ -1842,6 +1881,48 @@ class PowerManagerService extends IPowerManager.Stub } } + private int applyButtonState(int state) { + int brightness = -1; + if ((state & BATTERY_LOW_BIT) != 0) { + // do not override brightness if the battery is low + return state; + } + if (mButtonBrightnessOverride >= 0) { + brightness = mButtonBrightnessOverride; + } else if (mLightSensorButtonBrightness >= 0 && mUseSoftwareAutoBrightness) { + brightness = mLightSensorButtonBrightness; + } + if (brightness > 0) { + return state | BUTTON_BRIGHT_BIT; + } else if (brightness == 0) { + return state & ~BUTTON_BRIGHT_BIT; + } else { + return state; + } + } + + private int applyKeyboardState(int state) { + int brightness = -1; + if ((state & BATTERY_LOW_BIT) != 0) { + // do not override brightness if the battery is low + return state; + } + if (!mKeyboardVisible) { + brightness = 0; + } else if (mButtonBrightnessOverride >= 0) { + brightness = mButtonBrightnessOverride; + } else if (mLightSensorKeyboardBrightness >= 0 && mUseSoftwareAutoBrightness) { + brightness = mLightSensorKeyboardBrightness; + } + if (brightness > 0) { + return state | KEYBOARD_BRIGHT_BIT; + } else if (brightness == 0) { + return state & ~KEYBOARD_BRIGHT_BIT; + } else { + return state; + } + } + public boolean isScreenOn() { synchronized (mLocks) { return (mPowerState & SCREEN_ON_BIT) != 0; @@ -1888,7 +1969,7 @@ class PowerManagerService extends IPowerManager.Stub if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0) && (eventType == CHEEK_EVENT || eventType == TOUCH_EVENT)) { if (false) { - Log.d(TAG, "dropping cheek or short event mPokey=0x" + Integer.toHexString(mPokey)); + Slog.d(TAG, "dropping cheek or short event mPokey=0x" + Integer.toHexString(mPokey)); } return; } @@ -1897,22 +1978,22 @@ class PowerManagerService extends IPowerManager.Stub && (eventType == TOUCH_EVENT || eventType == TOUCH_UP_EVENT || eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT)) { if (false) { - Log.d(TAG, "dropping touch mPokey=0x" + Integer.toHexString(mPokey)); + Slog.d(TAG, "dropping touch mPokey=0x" + Integer.toHexString(mPokey)); } return; } if (false) { if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)) { - Log.d(TAG, "userActivity !!!");//, new RuntimeException()); + Slog.d(TAG, "userActivity !!!");//, new RuntimeException()); } else { - Log.d(TAG, "mPokey=0x" + Integer.toHexString(mPokey)); + Slog.d(TAG, "mPokey=0x" + Integer.toHexString(mPokey)); } } synchronized (mLocks) { if (mSpew) { - Log.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time + Slog.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time + " mUserActivityAllowed=" + mUserActivityAllowed + " mUserState=0x" + Integer.toHexString(mUserState) + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState) @@ -1921,7 +2002,7 @@ class PowerManagerService extends IPowerManager.Stub } // ignore user activity if we are in the process of turning off the screen if (isScreenTurningOffLocked()) { - Log.d(TAG, "ignoring user activity while turning off screen"); + Slog.d(TAG, "ignoring user activity while turning off screen"); return; } // Disable proximity sensor if if user presses power key while we are in the @@ -1950,7 +2031,7 @@ class PowerManagerService extends IPowerManager.Stub } finally { Binder.restoreCallingIdentity(ident); } - + mWakeLockState = mLocks.reactivateScreenLocksLocked(); setPowerState(mUserState | mWakeLockState, noChangeLights, WindowManagerPolicy.OFF_BECAUSE_OF_USER); @@ -1958,6 +2039,10 @@ class PowerManagerService extends IPowerManager.Stub } } } + + if (mPolicy != null) { + mPolicy.userActivity(); + } } private int getAutoBrightnessValue(int sensorValue, int[] values) { @@ -1971,7 +2056,7 @@ class PowerManagerService extends IPowerManager.Stub return values[i]; } catch (Exception e) { // guard against null pointer or index out of bounds errors - Log.e(TAG, "getAutoBrightnessValue", e); + Slog.e(TAG, "getAutoBrightnessValue", e); return 255; } } @@ -2002,15 +2087,40 @@ class PowerManagerService extends IPowerManager.Stub } }; + private void dockStateChanged(int state) { + synchronized (mLocks) { + mIsDocked = (state != Intent.EXTRA_DOCK_STATE_UNDOCKED); + if (mIsDocked) { + mHighestLightSensorValue = -1; + } + if ((mPowerState & SCREEN_ON_BIT) != 0) { + // force lights recalculation + int value = (int)mLightSensorValue; + mLightSensorValue = -1; + lightSensorChangedLocked(value); + } + } + } + private void lightSensorChangedLocked(int value) { if (mDebugLightSensor) { - Log.d(TAG, "lightSensorChangedLocked " + value); + Slog.d(TAG, "lightSensorChangedLocked " + value); + } + + // do not allow light sensor value to decrease + if (mHighestLightSensorValue < value) { + mHighestLightSensorValue = value; } if (mLightSensorValue != value) { mLightSensorValue = value; if ((mPowerState & BATTERY_LOW_BIT) == 0) { - int lcdValue = getAutoBrightnessValue(value, mLcdBacklightValues); + // use maximum light sensor value seen since screen went on for LCD to avoid flicker + // we only do this if we are undocked, since lighting should be stable when + // stationary in a dock. + int lcdValue = getAutoBrightnessValue( + (mIsDocked ? value : mHighestLightSensorValue), + mLcdBacklightValues); int buttonValue = getAutoBrightnessValue(value, mButtonBacklightValues); int keyboardValue; if (mKeyboardVisible) { @@ -2018,12 +2128,14 @@ class PowerManagerService extends IPowerManager.Stub } else { keyboardValue = 0; } - mLightSensorBrightness = lcdValue; + mLightSensorScreenBrightness = lcdValue; + mLightSensorButtonBrightness = buttonValue; + mLightSensorKeyboardBrightness = keyboardValue; if (mDebugLightSensor) { - Log.d(TAG, "lcdValue " + lcdValue); - Log.d(TAG, "buttonValue " + buttonValue); - Log.d(TAG, "keyboardValue " + keyboardValue); + Slog.d(TAG, "lcdValue " + lcdValue); + Slog.d(TAG, "buttonValue " + buttonValue); + Slog.d(TAG, "keyboardValue " + keyboardValue); } boolean startAnimation = false; @@ -2036,41 +2148,36 @@ class PowerManagerService extends IPowerManager.Stub } } else { int brightnessMode = (mAutoBrightessEnabled - ? HardwareService.BRIGHTNESS_MODE_SENSOR - : HardwareService.BRIGHTNESS_MODE_USER); - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, - lcdValue, brightnessMode); + ? LightsService.BRIGHTNESS_MODE_SENSOR + : LightsService.BRIGHTNESS_MODE_USER); + mLcdLight.setBrightness(lcdValue, brightnessMode); } } - if (ANIMATE_BUTTON_LIGHTS) { - if (mButtonBrightness.setTargetLocked(buttonValue, - AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS, - (int)mButtonBrightness.curValue)) { - startAnimation = true; + if (mButtonBrightnessOverride < 0) { + if (ANIMATE_BUTTON_LIGHTS) { + if (mButtonBrightness.setTargetLocked(buttonValue, + AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS, + (int)mButtonBrightness.curValue)) { + startAnimation = true; + } + } else { + mButtonLight.setBrightness(buttonValue); } - } else { - int brightnessMode = (mUseSoftwareAutoBrightness - ? HardwareService.BRIGHTNESS_MODE_SENSOR - : HardwareService.BRIGHTNESS_MODE_USER); - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, - buttonValue, brightnessMode); - } - if (ANIMATE_KEYBOARD_LIGHTS) { - if (mKeyboardBrightness.setTargetLocked(keyboardValue, - AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS, - (int)mKeyboardBrightness.curValue)) { - startAnimation = true; + } + if (mButtonBrightnessOverride < 0 || !mKeyboardVisible) { + if (ANIMATE_KEYBOARD_LIGHTS) { + if (mKeyboardBrightness.setTargetLocked(keyboardValue, + AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS, + (int)mKeyboardBrightness.curValue)) { + startAnimation = true; + } + } else { + mKeyboardLight.setBrightness(keyboardValue); } - } else { - int brightnessMode = (mUseSoftwareAutoBrightness - ? HardwareService.BRIGHTNESS_MODE_SENSOR - : HardwareService.BRIGHTNESS_MODE_USER); - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, - keyboardValue, brightnessMode); } if (startAnimation) { if (mDebugLightSensor) { - Log.i(TAG, "lightSensorChangedLocked scheduling light animator"); + Slog.i(TAG, "lightSensorChangedLocked scheduling light animator"); } mHandler.removeCallbacks(mLightAnimator); mHandler.post(mLightAnimator); @@ -2085,26 +2192,74 @@ class PowerManagerService extends IPowerManager.Stub */ public void goToSleep(long time) { + goToSleepWithReason(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER); + } + + /** + * The user requested that we go to sleep (probably with the power button). + * This overrides all wake locks that are held. + */ + public void goToSleepWithReason(long time, int reason) + { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); synchronized (mLocks) { - goToSleepLocked(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER); + goToSleepLocked(time, reason); } } - + /** - * Returns the time the screen has been on since boot, in millis. - * @return screen on time + * Reboot the device immediately, passing 'reason' (may be null) + * to the underlying __reboot system call. Should not return. */ - public long getScreenOnTime() { - synchronized (mLocks) { - if (mScreenOnStartTime == 0) { - return mScreenOnTime; - } else { - return SystemClock.elapsedRealtime() - mScreenOnStartTime + mScreenOnTime; + public void reboot(String reason) + { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); + + if (mHandler == null || !ActivityManagerNative.isSystemReady()) { + throw new IllegalStateException("Too early to call reboot()"); + } + + final String finalReason = reason; + Runnable runnable = new Runnable() { + public void run() { + synchronized (this) { + ShutdownThread.reboot(mContext, finalReason, false); + } + + } + }; + // ShutdownThread must run on a looper capable of displaying the UI. + mHandler.post(runnable); + + // PowerManager.reboot() is documented not to return so just wait for the inevitable. + synchronized (runnable) { + while (true) { + try { + runnable.wait(); + } catch (InterruptedException e) { + } } } } + /** + * Crash the runtime (causing a complete restart of the Android framework). + * Requires REBOOT permission. Mostly for testing. Should not return. + */ + public void crash(final String message) + { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); + Thread t = new Thread("PowerManagerService.crash()") { + public void run() { throw new RuntimeException(message); } + }; + try { + t.start(); + t.join(); + } catch (InterruptedException e) { + Log.wtf(TAG, e); + } + } + private void goToSleepLocked(long time, int reason) { if (mLastEventTime <= time) { @@ -2120,7 +2275,7 @@ class PowerManagerService extends IPowerManager.Stub numCleared++; } } - EventLog.writeEvent(LOG_POWER_SLEEP_REQUESTED, numCleared); + EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numCleared); mStillNeedSleepNotification = true; mUserState = SCREEN_OFF; setPowerState(SCREEN_OFF, false, reason); @@ -2136,11 +2291,11 @@ class PowerManagerService extends IPowerManager.Stub return SystemClock.elapsedRealtime() - mScreenOffTime; } } - + public void setKeyboardVisibility(boolean visible) { synchronized (mLocks) { if (mSpew) { - Log.d(TAG, "setKeyboardVisibility: " + visible); + Slog.d(TAG, "setKeyboardVisibility: " + visible); } if (mKeyboardVisible != visible) { mKeyboardVisible = visible; @@ -2169,7 +2324,7 @@ class PowerManagerService extends IPowerManager.Stub */ public void enableUserActivity(boolean enabled) { if (mSpew) { - Log.d(TAG, "enableUserActivity " + enabled); + Slog.d(TAG, "enableUserActivity " + enabled); } synchronized (mLocks) { mUserActivityAllowed = enabled; @@ -2202,7 +2357,7 @@ class PowerManagerService extends IPowerManager.Stub * */ private void setScreenOffTimeoutsLocked() { if ((mPokey & POKE_LOCK_SHORT_TIMEOUT) != 0) { - mKeylightDelay = mShortKeylightDelay; // Configurable via Gservices + mKeylightDelay = mShortKeylightDelay; // Configurable via secure settings mDimDelay = -1; mScreenOffDelay = 0; } else if ((mPokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0) { @@ -2210,7 +2365,10 @@ class PowerManagerService extends IPowerManager.Stub mDimDelay = -1; mScreenOffDelay = 0; } else { - int totalDelay = mTotalDelaySetting; + int totalDelay = mScreenOffTimeoutSetting; + if (totalDelay > mMaximumScreenOffTimeout) { + totalDelay = mMaximumScreenOffTimeout; + } mKeylightDelay = LONG_KEYLIGHT_DELAY; if (totalDelay < 0) { mScreenOffDelay = Integer.MAX_VALUE; @@ -2230,35 +2388,22 @@ class PowerManagerService extends IPowerManager.Stub } } if (mSpew) { - Log.d(TAG, "setScreenOffTimeouts mKeylightDelay=" + mKeylightDelay + Slog.d(TAG, "setScreenOffTimeouts mKeylightDelay=" + mKeylightDelay + " mDimDelay=" + mDimDelay + " mScreenOffDelay=" + mScreenOffDelay + " mDimScreen=" + mDimScreen); } } /** - * Refreshes cached Gservices settings. Called once on startup, and - * on subsequent Settings.Gservices.CHANGED_ACTION broadcasts (see - * GservicesChangedReceiver). + * Refreshes cached secure settings. Called once on startup, and + * on subsequent changes to secure settings. */ - private void updateGservicesValues() { - mShortKeylightDelay = Settings.Gservices.getInt( + private void updateSettingsValues() { + mShortKeylightDelay = Settings.Secure.getInt( mContext.getContentResolver(), - Settings.Gservices.SHORT_KEYLIGHT_DELAY_MS, + Settings.Secure.SHORT_KEYLIGHT_DELAY_MS, SHORT_KEYLIGHT_DELAY_DEFAULT); - // Log.i(TAG, "updateGservicesValues(): mShortKeylightDelay now " + mShortKeylightDelay); - } - - /** - * Receiver for the Gservices.CHANGED_ACTION broadcast intent, - * which tells us we need to refresh our cached Gservices settings. - */ - private class GservicesChangedReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - // Log.i(TAG, "GservicesChangedReceiver.onReceive(): " + intent); - updateGservicesValues(); - } + // Slog.i(TAG, "updateSettingsValues(): mShortKeylightDelay now " + mShortKeylightDelay); } private class LockList extends ArrayList<WakeLock> @@ -2306,7 +2451,7 @@ class PowerManagerService extends IPowerManager.Stub } return result; } - + int reactivateScreenLocksLocked() { int result = 0; @@ -2339,7 +2484,7 @@ class PowerManagerService extends IPowerManager.Stub } return mPolicy; } - + void systemReady() { mSensorManager = new SensorManager(mHandlerThread.getLooper()); mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); @@ -2349,9 +2494,21 @@ class PowerManagerService extends IPowerManager.Stub enableLightSensor(true); } + // wait until sensors are enabled before turning on screen. + // some devices will not activate the light sensor properly on boot + // unless we do this. + if (mUseSoftwareAutoBrightness) { + // turn the screen on + setPowerState(SCREEN_BRIGHT); + } else { + // turn everything on + setPowerState(ALL_BRIGHT); + } + synchronized (mLocks) { - Log.d(TAG, "system ready!"); + Slog.d(TAG, "system ready!"); mDoneBooting = true; + long identity = Binder.clearCallingIdentity(); try { mBatteryStats.noteScreenBrightness(getPreferredBrightness()); @@ -2365,7 +2522,7 @@ class PowerManagerService extends IPowerManager.Stub } void bootCompleted() { - Log.d(TAG, "bootCompleted"); + Slog.d(TAG, "bootCompleted"); synchronized (mLocks) { mBootCompleted = true; userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true); @@ -2394,17 +2551,14 @@ class PowerManagerService extends IPowerManager.Stub mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); // Don't let applications turn the screen all the way off brightness = Math.max(brightness, Power.BRIGHTNESS_DIM); - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, brightness, - HardwareService.BRIGHTNESS_MODE_USER); - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, - (mKeyboardVisible ? brightness : 0), HardwareService.BRIGHTNESS_MODE_USER); - mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, brightness, - HardwareService.BRIGHTNESS_MODE_USER); + mLcdLight.setBrightness(brightness); + mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0); + mButtonLight.setBrightness(brightness); long identity = Binder.clearCallingIdentity(); try { mBatteryStats.noteScreenBrightness(brightness); } catch (RemoteException e) { - Log.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e); + Slog.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e); } finally { Binder.restoreCallingIdentity(identity); } @@ -2427,9 +2581,14 @@ class PowerManagerService extends IPowerManager.Stub } } + public void setAttentionLight(boolean on, int color) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + mAttentionLight.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); + } + private void enableProximityLockLocked() { if (mDebugProximitySensor) { - Log.d(TAG, "enableProximityLockLocked"); + Slog.d(TAG, "enableProximityLockLocked"); } if (!mProximitySensorEnabled) { // clear calling identity so sensor manager battery stats are accurate @@ -2446,7 +2605,7 @@ class PowerManagerService extends IPowerManager.Stub private void disableProximityLockLocked() { if (mDebugProximitySensor) { - Log.d(TAG, "disableProximityLockLocked"); + Slog.d(TAG, "disableProximityLockLocked"); } if (mProximitySensorEnabled) { // clear calling identity so sensor manager battery stats are accurate @@ -2470,10 +2629,10 @@ class PowerManagerService extends IPowerManager.Stub private void proximityChangedLocked(boolean active) { if (mDebugProximitySensor) { - Log.d(TAG, "proximityChangedLocked, active: " + active); + Slog.d(TAG, "proximityChangedLocked, active: " + active); } if (!mProximitySensorEnabled) { - Log.d(TAG, "Ignoring proximity change after sensor is disabled"); + Slog.d(TAG, "Ignoring proximity change after sensor is disabled"); return; } if (active) { @@ -2496,7 +2655,7 @@ class PowerManagerService extends IPowerManager.Stub private void enableLightSensor(boolean enable) { if (mDebugLightSensor) { - Log.d(TAG, "enableLightSensor " + enable); + Slog.d(TAG, "enableLightSensor " + enable); } if (mSensorManager != null && mLightSensorEnabled != enable) { mLightSensorEnabled = enable; @@ -2531,7 +2690,7 @@ class PowerManagerService extends IPowerManager.Stub distance < mProximitySensor.getMaximumRange()); if (mDebugProximitySensor) { - Log.d(TAG, "mProximityListener.onSensorChanged active: " + active); + Slog.d(TAG, "mProximityListener.onSensorChanged active: " + active); } if (timeSinceLastEvent < PROXIMITY_SENSOR_DELAY) { // enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing @@ -2571,7 +2730,7 @@ class PowerManagerService extends IPowerManager.Stub int value = (int)event.values[0]; long milliseconds = SystemClock.elapsedRealtime(); if (mDebugLightSensor) { - Log.d(TAG, "onSensorChanged: light value: " + value); + Slog.d(TAG, "onSensorChanged: light value: " + value); } mHandler.removeCallbacks(mAutoBrightnessTask); if (mLightSensorValue != value) { diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java index ac3b723..a02c4e7 100644 --- a/services/java/com/android/server/ProcessStats.java +++ b/services/java/com/android/server/ProcessStats.java @@ -21,7 +21,7 @@ import static android.os.Process.*; import android.os.Process; import android.os.SystemClock; import android.util.Config; -import android.util.Log; +import android.util.Slog; import java.io.File; import java.io.FileInputStream; @@ -289,11 +289,11 @@ public class ProcessStats { mRelIdleTime = (int)(idletime - mBaseIdleTime); if (false) { - Log.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1] + Slog.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1] + " S:" + sysCpu[2] + " I:" + sysCpu[3] + " W:" + sysCpu[4] + " Q:" + sysCpu[5] + " O:" + sysCpu[6]); - Log.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime + Slog.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime + " I:" + mRelIdleTime + " Q:" + mRelIrqTime); } @@ -331,7 +331,7 @@ public class ProcessStats { // Update an existing process... st.added = false; curStatsIndex++; - if (localLOGV) Log.v(TAG, "Existing pid " + pid + ": " + st); + if (localLOGV) Slog.v(TAG, "Existing pid " + pid + ": " + st); final long[] procStats = mProcessStatsData; if (!Process.readProcFile(st.statFile.toString(), @@ -376,7 +376,7 @@ public class ProcessStats { st.rel_majfaults = (int)(majfaults - st.base_majfaults); st.base_minfaults = minfaults; st.base_majfaults = majfaults; - //Log.i("Load", "Stats changed " + name + " pid=" + st.pid + //Slog.i("Load", "Stats changed " + name + " pid=" + st.pid // + " name=" + st.name + " utime=" + utime // + " stime=" + stime); workingProcs.add(st); @@ -389,7 +389,7 @@ public class ProcessStats { allProcs.add(curStatsIndex, st); curStatsIndex++; NS++; - if (localLOGV) Log.v(TAG, "New pid " + pid + ": " + st); + if (localLOGV) Slog.v(TAG, "New pid " + pid + ": " + st); final String[] procStatsString = mProcessFullStatsStringData; final long[] procStats = mProcessFullStatsData; @@ -419,7 +419,7 @@ public class ProcessStats { } } - //Log.i("Load", "New process: " + st.pid + " " + st.name); + //Slog.i("Load", "New process: " + st.pid + " " + st.name); st.rel_utime = 0; st.rel_stime = 0; st.rel_minfaults = 0; @@ -440,7 +440,7 @@ public class ProcessStats { workingProcs.add(st); allProcs.remove(curStatsIndex); NS--; - if (localLOGV) Log.v(TAG, "Removed pid " + st.pid + ": " + st); + if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st); // Decrement the loop counter so that we process the current pid // again the next time through the loop. i--; @@ -458,7 +458,7 @@ public class ProcessStats { workingProcs.add(st); allProcs.remove(curStatsIndex); NS--; - if (localLOGV) Log.v(TAG, "Removed pid " + st.pid + ": " + st); + if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st); } return pids; @@ -523,11 +523,11 @@ public class ProcessStats { speed++; if (speed == MAX_SPEEDS) break; // No more if (localLOGV && out == null) { - Log.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1] + Slog.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1] + "\t" + tempTimes[speed - 1]); } } catch (NumberFormatException nfe) { - Log.i(TAG, "Unable to parse time_in_state"); + Slog.i(TAG, "Unable to parse time_in_state"); } } } diff --git a/services/java/com/android/server/RandomBlock.java b/services/java/com/android/server/RandomBlock.java index f7847ec..cc22bd9 100644 --- a/services/java/com/android/server/RandomBlock.java +++ b/services/java/com/android/server/RandomBlock.java @@ -16,7 +16,7 @@ package com.android.server; -import android.util.Log; +import android.util.Slog; import java.io.Closeable; import java.io.DataOutput; @@ -39,7 +39,7 @@ class RandomBlock { private RandomBlock() { } static RandomBlock fromFile(String filename) throws IOException { - if (DEBUG) Log.v(TAG, "reading from file " + filename); + if (DEBUG) Slog.v(TAG, "reading from file " + filename); InputStream stream = null; try { stream = new FileInputStream(filename); @@ -63,7 +63,7 @@ class RandomBlock { } void toFile(String filename) throws IOException { - if (DEBUG) Log.v(TAG, "writing to file " + filename); + if (DEBUG) Slog.v(TAG, "writing to file " + filename); RandomAccessFile out = null; try { out = new RandomAccessFile(filename, "rws"); @@ -95,7 +95,7 @@ class RandomBlock { } c.close(); } catch (IOException e) { - Log.w(TAG, "IOException thrown while closing Closeable", e); + Slog.w(TAG, "IOException thrown while closing Closeable", e); } } } diff --git a/services/java/com/android/server/RecognitionManagerService.java b/services/java/com/android/server/RecognitionManagerService.java new file mode 100644 index 0000000..8e55512 --- /dev/null +++ b/services/java/com/android/server/RecognitionManagerService.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import com.android.internal.content.PackageMonitor; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Binder; +import android.provider.Settings; +import android.speech.RecognitionService; +import android.text.TextUtils; +import android.util.Slog; + +import java.util.List; + +public class RecognitionManagerService extends Binder { + final static String TAG = "RecognitionManagerService"; + + final Context mContext; + final MyPackageMonitor mMonitor; + + class MyPackageMonitor extends PackageMonitor { + public void onSomePackagesChanged() { + ComponentName comp = getCurRecognizer(); + if (comp == null) { + if (anyPackagesAppearing()) { + comp = findAvailRecognizer(null); + if (comp != null) { + setCurRecognizer(comp); + } + } + return; + } + + int change = isPackageDisappearing(comp.getPackageName()); + if (change == PACKAGE_PERMANENT_CHANGE + || change == PACKAGE_TEMPORARY_CHANGE) { + setCurRecognizer(findAvailRecognizer(null)); + + } else if (isPackageModified(comp.getPackageName())) { + setCurRecognizer(findAvailRecognizer(comp.getPackageName())); + } + } + } + + RecognitionManagerService(Context context) { + mContext = context; + mMonitor = new MyPackageMonitor(); + mMonitor.register(context, true); + } + + public void systemReady() { + ComponentName comp = getCurRecognizer(); + if (comp != null) { + // See if the current recognizer is no longer available. + try { + mContext.getPackageManager().getServiceInfo(comp, 0); + } catch (NameNotFoundException e) { + setCurRecognizer(null); + } + } else { + comp = findAvailRecognizer(null); + if (comp != null) { + setCurRecognizer(comp); + } + } + } + + ComponentName findAvailRecognizer(String prefPackage) { + List<ResolveInfo> available = + mContext.getPackageManager().queryIntentServices( + new Intent(RecognitionService.SERVICE_INTERFACE), 0); + int numAvailable = available.size(); + + if (numAvailable == 0) { + Slog.w(TAG, "no available voice recognition services found"); + return null; + } else { + if (prefPackage != null) { + for (int i=0; i<numAvailable; i++) { + ServiceInfo serviceInfo = available.get(i).serviceInfo; + if (prefPackage.equals(serviceInfo.packageName)) { + return new ComponentName(serviceInfo.packageName, serviceInfo.name); + } + } + } + if (numAvailable > 1) { + Slog.w(TAG, "more than one voice recognition service found, picking first"); + } + + ServiceInfo serviceInfo = available.get(0).serviceInfo; + return new ComponentName(serviceInfo.packageName, serviceInfo.name); + } + } + + ComponentName getCurRecognizer() { + String curRecognizer = Settings.Secure.getString( + mContext.getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE); + if (TextUtils.isEmpty(curRecognizer)) { + return null; + } + return ComponentName.unflattenFromString(curRecognizer); + } + + void setCurRecognizer(ComponentName comp) { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE, + comp != null ? comp.flattenToShortString() : ""); + } +} diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java index 4dfeb9d..9f5718f 100644 --- a/services/java/com/android/server/SensorService.java +++ b/services/java/com/android/server/SensorService.java @@ -23,8 +23,12 @@ import android.os.Bundle; import android.os.RemoteException; import android.os.IBinder; import android.util.Config; -import android.util.Log; +import android.util.Slog; +import android.util.PrintWriterPrinter; +import android.util.Printer; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; import com.android.internal.app.IBatteryStats; @@ -43,6 +47,7 @@ class SensorService extends ISensorService.Stub { private static final boolean DEBUG = false; private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; private static final int SENSOR_DISABLE = -1; + private int mCurrentDelay = 0; /** * Battery statistics to be updated when sensors are enabled and disabled. @@ -51,17 +56,19 @@ class SensorService extends ISensorService.Stub { private final class Listener implements IBinder.DeathRecipient { final IBinder mToken; + final int mUid; int mSensors = 0; int mDelay = 0x7FFFFFFF; - Listener(IBinder token) { + Listener(IBinder token, int uid) { mToken = token; + mUid = uid; } void addSensor(int sensor, int delay) { mSensors |= (1<<sensor); - if (mDelay > delay) + if (delay < mDelay) mDelay = delay; } @@ -74,7 +81,7 @@ class SensorService extends ISensorService.Stub { } public void binderDied() { - if (localLOGV) Log.d(TAG, "sensor listener died"); + if (localLOGV) Slog.d(TAG, "sensor listener died"); synchronized(mListeners) { mListeners.remove(this); mToken.unlinkToDeath(this, 0); @@ -83,16 +90,20 @@ class SensorService extends ISensorService.Stub { for (int sensor=0 ; sensor<32 && mSensors!=0 ; sensor++) { if (hasSensor(sensor)) { removeSensor(sensor); + deactivateIfUnusedLocked(sensor); try { - deactivateIfUnusedLocked(sensor); + mBatteryStats.noteStopSensor(mUid, sensor); } catch (RemoteException e) { - Log.w(TAG, "RemoteException in binderDied"); + // oops. not a big deal. } } } if (mListeners.size() == 0) { _sensors_control_wake(); _sensors_control_close(); + } else { + // TODO: we should recalculate the delay, since removing + // a listener may increase the overall rate. } mListeners.notify(); } @@ -101,7 +112,7 @@ class SensorService extends ISensorService.Stub { @SuppressWarnings("unused") public SensorService(Context context) { - if (localLOGV) Log.d(TAG, "SensorService startup"); + if (localLOGV) Slog.d(TAG, "SensorService startup"); _sensors_control_init(); } @@ -113,86 +124,151 @@ class SensorService extends ISensorService.Stub { } public boolean enableSensor(IBinder binder, String name, int sensor, int enable) - throws RemoteException { - if (localLOGV) Log.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable); + throws RemoteException { - // Inform battery statistics service of status change - int uid = Binder.getCallingUid(); - long identity = Binder.clearCallingIdentity(); - if (enable == SENSOR_DISABLE) { - mBatteryStats.noteStopSensor(uid, sensor); - } else { - mBatteryStats.noteStartSensor(uid, sensor); - } - Binder.restoreCallingIdentity(identity); + if (localLOGV) Slog.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable); if (binder == null) { - Log.w(TAG, "listener is null (sensor=" + name + ", id=" + sensor + ")"); + Slog.e(TAG, "listener is null (sensor=" + name + ", id=" + sensor + ")"); + return false; + } + + if (enable < 0 && (enable != SENSOR_DISABLE)) { + Slog.e(TAG, "invalid enable parameter (enable=" + enable + + ", sensor=" + name + ", id=" + sensor + ")"); return false; } + boolean res; + int uid = Binder.getCallingUid(); synchronized(mListeners) { - if (enable!=SENSOR_DISABLE && !_sensors_control_activate(sensor, true)) { - Log.w(TAG, "could not enable sensor " + sensor); - return false; - } - - Listener l = null; - int minDelay = enable; - for (Listener listener : mListeners) { - if (binder == listener.mToken) { - l = listener; + res = enableSensorInternalLocked(binder, uid, name, sensor, enable); + if (res == true) { + // Inform battery statistics service of status change + long identity = Binder.clearCallingIdentity(); + if (enable == SENSOR_DISABLE) { + mBatteryStats.noteStopSensor(uid, sensor); + } else { + mBatteryStats.noteStartSensor(uid, sensor); } - if (minDelay > listener.mDelay) - minDelay = listener.mDelay; + Binder.restoreCallingIdentity(identity); + } + } + return res; + } + + private boolean enableSensorInternalLocked(IBinder binder, int uid, + String name, int sensor, int enable) throws RemoteException { + + // check if we have this listener + Listener l = null; + for (Listener listener : mListeners) { + if (binder == listener.mToken) { + l = listener; + break; } - - if (l == null && enable!=SENSOR_DISABLE) { - l = new Listener(binder); + } + + if (enable != SENSOR_DISABLE) { + // Activate the requested sensor + if (_sensors_control_activate(sensor, true) == false) { + Slog.w(TAG, "could not enable sensor " + sensor); + return false; + } + + if (l == null) { + /* + * we don't have a listener for this binder yet, so + * create a new one and add it to the list. + */ + l = new Listener(binder, uid); binder.linkToDeath(l, 0); mListeners.add(l); mListeners.notify(); } - + + // take note that this sensor is now used by this client + l.addSensor(sensor, enable); + + } else { + if (l == null) { - // 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 + ")"); + /* + * This client isn't in the list, this usually happens + * when enabling the sensor failed, but the client + * didn't handle the error and later tries to shut that + * sensor off. + */ + Slog.w(TAG, "listener with binder " + binder + + ", doesn't exist (sensor=" + name + + ", id=" + sensor + ")"); return false; } - - if (minDelay >= 0) { - _sensors_control_set_delay(minDelay); - } - - if (enable != SENSOR_DISABLE) { - l.addSensor(sensor, enable); - } else { - l.removeSensor(sensor); - deactivateIfUnusedLocked(sensor); - if (l.mSensors == 0) { - mListeners.remove(l); - binder.unlinkToDeath(l, 0); - mListeners.notify(); + + // remove this sensor from this client + l.removeSensor(sensor); + + // see if we need to deactivate this sensors= + deactivateIfUnusedLocked(sensor); + + // if the listener doesn't have any more sensors active + // we can get rid of it + if (l.mSensors == 0) { + // we won't need this death notification anymore + binder.unlinkToDeath(l, 0); + // remove the listener from the list + mListeners.remove(l); + // and if the list is empty, turn off the whole sensor h/w + if (mListeners.size() == 0) { + _sensors_control_wake(); + _sensors_control_close(); } + mListeners.notify(); } - - if (mListeners.size() == 0) { - _sensors_control_wake(); - _sensors_control_close(); - } - } + } + + // calculate and set the new delay + int minDelay = 0x7FFFFFFF; + for (Listener listener : mListeners) { + if (listener.mDelay < minDelay) + minDelay = listener.mDelay; + } + if (minDelay != 0x7FFFFFFF) { + mCurrentDelay = minDelay; + _sensors_control_set_delay(minDelay); + } + return true; } - private void deactivateIfUnusedLocked(int sensor) throws RemoteException { + private void deactivateIfUnusedLocked(int sensor) { int size = mListeners.size(); for (int i=0 ; i<size ; i++) { - if (mListeners.get(i).hasSensor(sensor)) + if (mListeners.get(i).hasSensor(sensor)) { + // this sensor is still in use, don't turn it off return; + } + } + if (_sensors_control_activate(sensor, false) == false) { + Slog.w(TAG, "could not disable sensor " + sensor); + } + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + synchronized (mListeners) { + Printer pr = new PrintWriterPrinter(pw); + int c = 0; + pr.println(mListeners.size() + " listener(s), delay=" + mCurrentDelay + " ms"); + for (Listener l : mListeners) { + pr.println("listener[" + c + "] " + + "sensors=0x" + Integer.toString(l.mSensors, 16) + + ", uid=" + l.mUid + + ", delay=" + + l.mDelay + " ms"); + c++; + } } - _sensors_control_activate(sensor, false); } private ArrayList<Listener> mListeners = new ArrayList<Listener>(); diff --git a/services/java/com/android/server/ShutdownActivity.java b/services/java/com/android/server/ShutdownActivity.java index 7f0e90d..64b9c5d 100644 --- a/services/java/com/android/server/ShutdownActivity.java +++ b/services/java/com/android/server/ShutdownActivity.java @@ -21,7 +21,7 @@ import android.content.BroadcastReceiver; import android.content.Intent; import android.os.Bundle; import android.os.Handler; -import android.util.Log; +import android.util.Slog; import com.android.internal.app.ShutdownThread; public class ShutdownActivity extends Activity { @@ -34,7 +34,7 @@ public class ShutdownActivity extends Activity { super.onCreate(savedInstanceState); mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false); - Log.i(TAG, "onCreate(): confirm=" + mConfirm); + Slog.i(TAG, "onCreate(): confirm=" + mConfirm); Handler h = new Handler(); h.post(new Runnable() { diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java index 67abe55..fff1874 100644 --- a/services/java/com/android/server/SystemBackupAgent.java +++ b/services/java/com/android/server/SystemBackupAgent.java @@ -16,17 +16,17 @@ package com.android.server; -import android.backup.AbsoluteFileBackupHelper; -import android.backup.BackupDataInput; -import android.backup.BackupDataInputStream; -import android.backup.BackupDataOutput; -import android.backup.BackupHelper; -import android.backup.BackupHelperAgent; +import android.app.backup.AbsoluteFileBackupHelper; +import android.app.backup.BackupDataInput; +import android.app.backup.BackupDataInputStream; +import android.app.backup.BackupDataOutput; +import android.app.backup.BackupHelper; +import android.app.backup.BackupAgentHelper; import android.content.Context; import android.os.ParcelFileDescriptor; import android.os.ServiceManager; import android.os.SystemService; -import android.util.Log; +import android.util.Slog; import java.io.File; import java.io.IOException; @@ -34,7 +34,7 @@ import java.io.IOException; /** * Backup agent for various system-managed data, currently just the system wallpaper */ -public class SystemBackupAgent extends BackupHelperAgent { +public class SystemBackupAgent extends BackupAgentHelper { private static final String TAG = "SystemBackupAgent"; // These paths must match what the WallpaperManagerService uses @@ -77,7 +77,7 @@ public class SystemBackupAgent extends BackupHelperAgent { } catch (IOException ex) { // If there was a failure, delete everything for the wallpaper, this is too aggresive, // but this is hopefully a rare failure. - Log.d(TAG, "restore failed", ex); + Slog.d(TAG, "restore failed", ex); (new File(WALLPAPER_IMAGE)).delete(); (new File(WALLPAPER_INFO)).delete(); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b8cf844..9d5d035 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -18,9 +18,11 @@ package com.android.server; import com.android.server.am.ActivityManagerService; import com.android.server.status.StatusBarService; +import com.android.internal.os.BinderInternal; import com.android.internal.os.SamplingProfilerIntegration; import dalvik.system.VMRuntime; +import dalvik.system.Zygote; import android.app.ActivityManagerNative; import android.bluetooth.BluetoothAdapter; @@ -40,9 +42,10 @@ import android.server.BluetoothA2dpService; import android.server.BluetoothService; import android.server.search.SearchManagerService; import android.util.EventLog; -import android.util.Log; +import android.util.Slog; import android.accounts.AccountManagerService; +import java.io.File; import java.util.Timer; import java.util.TimerTask; @@ -69,21 +72,21 @@ class ServerThread extends Thread { @Override public void run() { - EventLog.writeEvent(LOG_BOOT_PROGRESS_SYSTEM_RUN, + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis()); - ActivityManagerService.prepareTraceFile(false); // create dir - Looper.prepare(); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); + BinderInternal.disableBackgroundScheduling(true); + String factoryTestStr = SystemProperties.get("ro.factorytest"); int factoryTest = "".equals(factoryTestStr) ? SystemServer.FACTORY_TEST_OFF : Integer.parseInt(factoryTestStr); - HardwareService hardware = null; + LightsService lights = null; PowerManagerService power = null; BatteryService battery = null; ConnectivityService connectivity = null; @@ -94,25 +97,28 @@ class ServerThread extends Thread { BluetoothA2dpService bluetoothA2dp = null; HeadsetObserver headset = null; DockObserver dock = null; + UiModeManagerService uiMode = null; + RecognitionManagerService recognition = null; + ThrottleService throttle = null; // Critical services... try { - Log.i(TAG, "Entropy Service"); + Slog.i(TAG, "Entropy Service"); ServiceManager.addService("entropy", new EntropyService()); - Log.i(TAG, "Power Manager"); + Slog.i(TAG, "Power Manager"); power = new PowerManagerService(); ServiceManager.addService(Context.POWER_SERVICE, power); - Log.i(TAG, "Activity Manager"); + Slog.i(TAG, "Activity Manager"); context = ActivityManagerService.main(factoryTest); - Log.i(TAG, "Telephony Registry"); + Slog.i(TAG, "Telephony Registry"); ServiceManager.addService("telephony.registry", new TelephonyRegistry(context)); AttributeCache.init(context); - Log.i(TAG, "Package Manager"); + Slog.i(TAG, "Package Manager"); pm = PackageManagerService.main(context, factoryTest != SystemServer.FACTORY_TEST_OFF); @@ -122,45 +128,47 @@ class ServerThread extends Thread { // The AccountManager must come before the ContentService try { - Log.i(TAG, "Account Manager"); + Slog.i(TAG, "Account Manager"); ServiceManager.addService(Context.ACCOUNT_SERVICE, new AccountManagerService(context)); } catch (Throwable e) { - Log.e(TAG, "Failure starting Account Manager", e); + Slog.e(TAG, "Failure starting Account Manager", e); } - Log.i(TAG, "Content Manager"); + Slog.i(TAG, "Content Manager"); ContentService.main(context, factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL); - Log.i(TAG, "System Content Providers"); + Slog.i(TAG, "System Content Providers"); ActivityManagerService.installSystemProviders(); - Log.i(TAG, "Battery Service"); + Slog.i(TAG, "Battery Service"); battery = new BatteryService(context); ServiceManager.addService("battery", battery); - Log.i(TAG, "Hardware Service"); - hardware = new HardwareService(context); - ServiceManager.addService("hardware", hardware); + Slog.i(TAG, "Lights Service"); + lights = new LightsService(context); + + Slog.i(TAG, "Vibrator Service"); + ServiceManager.addService("vibrator", new VibratorService(context)); // only initialize the power service after we have started the - // hardware service, content providers and the battery service. - power.init(context, hardware, ActivityManagerService.getDefault(), battery); + // lights service, content providers and the battery service. + power.init(context, lights, ActivityManagerService.getDefault(), battery); - Log.i(TAG, "Alarm Manager"); + Slog.i(TAG, "Alarm Manager"); AlarmManagerService alarm = new AlarmManagerService(context); ServiceManager.addService(Context.ALARM_SERVICE, alarm); - Log.i(TAG, "Init Watchdog"); + Slog.i(TAG, "Init Watchdog"); Watchdog.getInstance().init(context, battery, power, alarm, ActivityManagerService.self()); // Sensor Service is needed by Window Manager, so this goes first - Log.i(TAG, "Sensor Service"); + Slog.i(TAG, "Sensor Service"); ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context)); - Log.i(TAG, "Window Manager"); + Slog.i(TAG, "Window Manager"); wm = WindowManagerService.main(context, power, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL); ServiceManager.addService(Context.WINDOW_SERVICE, wm); @@ -172,13 +180,13 @@ class ServerThread extends Thread { // TODO: Use a more reliable check to see if this product should // support Bluetooth - see bug 988521 if (SystemProperties.get("ro.kernel.qemu").equals("1")) { - Log.i(TAG, "Registering null Bluetooth Service (emulator)"); + Slog.i(TAG, "Registering null Bluetooth Service (emulator)"); ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, null); } else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) { - Log.i(TAG, "Registering null Bluetooth Service (factory test)"); + Slog.i(TAG, "Registering null Bluetooth Service (factory test)"); ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, null); } else { - Log.i(TAG, "Bluetooth Service"); + Slog.i(TAG, "Bluetooth Service"); bluetooth = new BluetoothService(context); ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, bluetooth); bluetooth.initAfterRegistration(); @@ -194,168 +202,219 @@ class ServerThread extends Thread { } } catch (RuntimeException e) { - Log.e("System", "Failure starting core service", e); + Slog.e("System", "Failure starting core service", e); } + DevicePolicyManagerService devicePolicy = null; StatusBarService statusBar = null; InputMethodManagerService imm = null; AppWidgetService appWidget = null; NotificationManagerService notification = null; WallpaperManagerService wallpaper = null; + LocationManagerService location = null; if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { try { - Log.i(TAG, "Status Bar"); + Slog.i(TAG, "Device Policy"); + devicePolicy = new DevicePolicyManagerService(context); + ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting DevicePolicyService", e); + } + + try { + Slog.i(TAG, "Status Bar"); statusBar = new StatusBarService(context); - ServiceManager.addService("statusbar", statusBar); + ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar); } catch (Throwable e) { - Log.e(TAG, "Failure starting StatusBarService", e); + Slog.e(TAG, "Failure starting StatusBarService", e); } try { - Log.i(TAG, "Clipboard Service"); - ServiceManager.addService("clipboard", new ClipboardService(context)); + Slog.i(TAG, "Clipboard Service"); + ServiceManager.addService(Context.CLIPBOARD_SERVICE, + new ClipboardService(context)); } catch (Throwable e) { - Log.e(TAG, "Failure starting Clipboard Service", e); + Slog.e(TAG, "Failure starting Clipboard Service", e); } try { - Log.i(TAG, "Input Method Service"); + Slog.i(TAG, "Input Method Service"); imm = new InputMethodManagerService(context, statusBar); ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm); } catch (Throwable e) { - Log.e(TAG, "Failure starting Input Manager Service", e); + Slog.e(TAG, "Failure starting Input Manager Service", e); } try { - Log.i(TAG, "NetStat Service"); + Slog.i(TAG, "NetStat Service"); ServiceManager.addService("netstat", new NetStatService(context)); } catch (Throwable e) { - Log.e(TAG, "Failure starting NetStat Service", e); + Slog.e(TAG, "Failure starting NetStat Service", e); + } + + try { + Slog.i(TAG, "NetworkManagement Service"); + ServiceManager.addService( + Context.NETWORKMANAGEMENT_SERVICE, new NetworkManagementService(context)); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting NetworkManagement Service", e); } try { - Log.i(TAG, "Connectivity Service"); + Slog.i(TAG, "Connectivity Service"); connectivity = ConnectivityService.getInstance(context); ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity); } catch (Throwable e) { - Log.e(TAG, "Failure starting Connectivity Service", e); + Slog.e(TAG, "Failure starting Connectivity Service", e); } try { - Log.i(TAG, "Accessibility Manager"); + Slog.i(TAG, "Throttle Service"); + throttle = new ThrottleService(context); + ServiceManager.addService( + Context.THROTTLE_SERVICE, throttle); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting ThrottleService", e); + } + + try { + Slog.i(TAG, "Accessibility Manager"); ServiceManager.addService(Context.ACCESSIBILITY_SERVICE, new AccessibilityManagerService(context)); } catch (Throwable e) { - Log.e(TAG, "Failure starting Accessibility Manager", e); + Slog.e(TAG, "Failure starting Accessibility Manager", e); } try { - Log.i(TAG, "Notification Manager"); - notification = new NotificationManagerService(context, statusBar, hardware); - ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification); + /* + * NotificationManagerService is dependant on MountService, + * (for media / usb notifications) so we must start MountService first. + */ + Slog.i(TAG, "Mount Service"); + ServiceManager.addService("mount", new MountService(context)); } catch (Throwable e) { - Log.e(TAG, "Failure starting Notification Manager", e); + Slog.e(TAG, "Failure starting Mount Service", e); } try { - // MountService must start after NotificationManagerService - Log.i(TAG, "Mount Service"); - ServiceManager.addService("mount", new MountService(context)); + Slog.i(TAG, "Notification Manager"); + notification = new NotificationManagerService(context, statusBar, lights); + ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification); } catch (Throwable e) { - Log.e(TAG, "Failure starting Mount Service", e); + Slog.e(TAG, "Failure starting Notification Manager", e); } try { - Log.i(TAG, "Device Storage Monitor"); + Slog.i(TAG, "Device Storage Monitor"); ServiceManager.addService(DeviceStorageMonitorService.SERVICE, new DeviceStorageMonitorService(context)); } catch (Throwable e) { - Log.e(TAG, "Failure starting DeviceStorageMonitor service", e); + Slog.e(TAG, "Failure starting DeviceStorageMonitor service", e); } try { - Log.i(TAG, "Location Manager"); - ServiceManager.addService(Context.LOCATION_SERVICE, new LocationManagerService(context)); + Slog.i(TAG, "Location Manager"); + location = new LocationManagerService(context); + ServiceManager.addService(Context.LOCATION_SERVICE, location); } catch (Throwable e) { - Log.e(TAG, "Failure starting Location Manager", e); + Slog.e(TAG, "Failure starting Location Manager", e); } try { - Log.i(TAG, "Search Service"); - ServiceManager.addService( Context.SEARCH_SERVICE, new SearchManagerService(context) ); + Slog.i(TAG, "Search Service"); + ServiceManager.addService(Context.SEARCH_SERVICE, + new SearchManagerService(context)); } catch (Throwable e) { - Log.e(TAG, "Failure starting Search Service", e); + Slog.e(TAG, "Failure starting Search Service", e); } if (INCLUDE_DEMO) { - Log.i(TAG, "Installing demo data..."); + Slog.i(TAG, "Installing demo data..."); (new DemoThread(context)).start(); } try { - Log.i(TAG, "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)); - } + Slog.i(TAG, "DropBox Service"); + ServiceManager.addService(Context.DROPBOX_SERVICE, + new DropBoxManagerService(context, new File("/data/system/dropbox"))); } catch (Throwable e) { - Log.e(TAG, "Failure starting Checkin Service", e); + Slog.e(TAG, "Failure starting DropBoxManagerService", e); } try { - Log.i(TAG, "Wallpaper Service"); + Slog.i(TAG, "Wallpaper Service"); wallpaper = new WallpaperManagerService(context); ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper); } catch (Throwable e) { - Log.e(TAG, "Failure starting Wallpaper Service", e); + Slog.e(TAG, "Failure starting Wallpaper Service", e); } try { - Log.i(TAG, "Audio Service"); + Slog.i(TAG, "Audio Service"); ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context)); } catch (Throwable e) { - Log.e(TAG, "Failure starting Audio Service", e); + Slog.e(TAG, "Failure starting Audio Service", e); } try { - Log.i(TAG, "Headset Observer"); + Slog.i(TAG, "Headset Observer"); // Listen for wired headset changes headset = new HeadsetObserver(context); } catch (Throwable e) { - Log.e(TAG, "Failure starting HeadsetObserver", e); + Slog.e(TAG, "Failure starting HeadsetObserver", e); } try { - Log.i(TAG, "Dock Observer"); + Slog.i(TAG, "Dock Observer"); // Listen for dock station changes dock = new DockObserver(context, power); } catch (Throwable e) { - Log.e(TAG, "Failure starting DockObserver", e); + Slog.e(TAG, "Failure starting DockObserver", e); } try { - Log.i(TAG, "Backup Service"); - ServiceManager.addService(Context.BACKUP_SERVICE, new BackupManagerService(context)); + Slog.i(TAG, "UI Mode Manager Service"); + // Listen for dock station changes + uiMode = new UiModeManagerService(context); } catch (Throwable e) { - Log.e(TAG, "Failure starting Backup Service", e); + Slog.e(TAG, "Failure starting UiModeManagerService", e); } try { - Log.i(TAG, "AppWidget Service"); + Slog.i(TAG, "Backup Service"); + ServiceManager.addService(Context.BACKUP_SERVICE, + new BackupManagerService(context)); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting Backup Service", e); + } + + try { + Slog.i(TAG, "AppWidget Service"); appWidget = new AppWidgetService(context); ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget); } catch (Throwable e) { - Log.e(TAG, "Failure starting AppWidget Service", e); + Slog.e(TAG, "Failure starting AppWidget Service", e); + } + + try { + Slog.i(TAG, "Recognition Service"); + recognition = new RecognitionManagerService(context); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting Recognition Service", e); } try { com.android.server.status.StatusBarPolicy.installIcons(context, statusBar); } catch (Throwable e) { - Log.e(TAG, "Failure installing status bar icons", e); + Slog.e(TAG, "Failure installing status bar icons", e); + } + + try { + Slog.i(TAG, "DiskStats Service"); + ServiceManager.addService("diskstats", new DiskStatsService(context)); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting DiskStats Service", e); } } @@ -373,12 +432,23 @@ class ServerThread extends Thread { if (safeMode) { try { ActivityManagerNative.getDefault().enterSafeMode(); + // Post the safe mode state in the Zygote class + Zygote.systemInSafeMode = true; + // Disable the JIT for the system_server process + VMRuntime.getRuntime().disableJitCompilation(); } catch (RemoteException e) { } + } else { + // Enable the JIT for the system_server process + VMRuntime.getRuntime().startJitCompilation(); } - + // It is now time to start up the app processes... + if (devicePolicy != null) { + devicePolicy.systemReady(); + } + if (notification != null) { notification.systemReady(); } @@ -397,10 +467,14 @@ class ServerThread extends Thread { final BatteryService batteryF = battery; final ConnectivityService connectivityF = connectivity; final DockObserver dockF = dock; + final ThrottleService throttleF = throttle; + final UiModeManagerService uiModeF = uiMode; final AppWidgetService appWidgetF = appWidget; final WallpaperManagerService wallpaperF = wallpaper; final InputMethodManagerService immF = imm; - + final RecognitionManagerService recognitionF = recognition; + final LocationManagerService locationF = location; + // We now tell the activity manager it is okay to run third party // code. It will call back into us once it has gotten to the state // where third party code can really run (but before it has actually @@ -409,24 +483,28 @@ class ServerThread extends Thread { ((ActivityManagerService)ActivityManagerNative.getDefault()) .systemReady(new Runnable() { public void run() { - Log.i(TAG, "Making services ready"); - + Slog.i(TAG, "Making services ready"); + if (batteryF != null) batteryF.systemReady(); if (connectivityF != null) connectivityF.systemReady(); if (dockF != null) dockF.systemReady(); + if (uiModeF != null) uiModeF.systemReady(); + if (recognitionF != null) recognitionF.systemReady(); Watchdog.getInstance().start(); // It is now okay to let the various system services start their // third party code... - + if (appWidgetF != null) appWidgetF.systemReady(safeMode); if (wallpaperF != null) wallpaperF.systemReady(); if (immF != null) immF.systemReady(); + if (locationF != null) locationF.systemReady(); + if (throttleF != null) throttleF.systemReady(); } }); - + Looper.loop(); - Log.d(TAG, "System ServerThread is exiting!"); + Slog.d(TAG, "System ServerThread is exiting!"); } } @@ -451,7 +529,7 @@ class DemoThread extends Thread dataset.add(mContext); } } catch (Throwable e) { - Log.e("SystemServer", "Failure installing demo data", e); + Slog.e("SystemServer", "Failure installing demo data", e); } } @@ -492,13 +570,13 @@ public class SystemServer // The system server has to run all of the time, so it needs to be // as efficient as possible with its memory usage. VMRuntime.getRuntime().setTargetHeapUtilization(0.8f); - + System.loadLibrary("android_servers"); init1(args); } public static final void init2() { - Log.i(TAG, "Entered the Android system server!"); + Slog.i(TAG, "Entered the Android system server!"); Thread thr = new ServerThread(); thr.setName("android.server.ServerThread"); thr.start(); diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index 47cb6ad..664dfa5 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -29,7 +29,7 @@ import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.text.TextUtils; -import android.util.Log; +import android.util.Slog; import java.util.ArrayList; import java.io.FileDescriptor; @@ -124,7 +124,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { public void listen(String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow) { - // Log.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + + // Slog.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + // Integer.toHexString(events)); if (events != 0) { /* Checks permission and throws Security exception */ @@ -485,6 +485,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); Bundle data = new Bundle(); state.fillInNotifierBundle(data); intent.putExtras(data); @@ -502,6 +503,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); Bundle data = new Bundle(); signalStrength.fillInNotifierBundle(data); intent.putExtras(data); @@ -523,6 +525,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString()); if (!TextUtils.isEmpty(incomingNumber)) { intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber); @@ -537,6 +540,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { // status bar takes care of that after taking into account all of the // required info. Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertDataState(state).toString()); if (!isDataConnectivityPossible) { intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, true); @@ -559,6 +563,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private void broadcastDataConnectionFailed(String reason) { Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(Phone.FAILURE_REASON_KEY, reason); mContext.sendStickyBroadcast(intent); } @@ -570,7 +575,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } String msg = "Modify Phone State Permission Denial: " + method + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); - Log.w(TAG, msg); + Slog.w(TAG, msg); return false; } diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java new file mode 100644 index 0000000..6a5bbd2 --- /dev/null +++ b/services/java/com/android/server/ThrottleService.java @@ -0,0 +1,1040 @@ +/* + * 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.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.net.IThrottleManager; +import android.net.SntpClient; +import android.net.ThrottleManager; +import android.os.Binder; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.util.Slog; + +import com.android.internal.R; +import com.android.internal.telephony.TelephonyProperties; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Properties; +import java.util.Random; + +// TODO - add comments - reference the ThrottleManager for public API +public class ThrottleService extends IThrottleManager.Stub { + + private static final String TESTING_ENABLED_PROPERTY = "persist.throttle.testing"; + + private static final String TAG = "ThrottleService"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + private Handler mHandler; + private HandlerThread mThread; + + private Context mContext; + + private static final int INITIAL_POLL_DELAY_SEC = 90; + private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1; + private static final int TESTING_RESET_PERIOD_SEC = 60 * 10; + private static final long TESTING_THRESHOLD = 1 * 1024 * 1024; + + private int mPolicyPollPeriodSec; + private long mPolicyThreshold; + private int mPolicyThrottleValue; + private int mPolicyResetDay; // 1-28 + private int mPolicyNotificationsAllowedMask; + + private long mLastRead; // read byte count from last poll + private long mLastWrite; // write byte count from last poll + + private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL"; + private static int POLL_REQUEST = 0; + private PendingIntent mPendingPollIntent; + private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET"; + private static int RESET_REQUEST = 1; + private PendingIntent mPendingResetIntent; + + private INetworkManagementService mNMService; + private AlarmManager mAlarmManager; + private NotificationManager mNotificationManager; + + private DataRecorder mRecorder; + + private String mIface; + + private static final int NOTIFICATION_WARNING = 2; + + private Notification mThrottlingNotification; + private boolean mWarningNotificationSent = false; + + private SettingsObserver mSettingsObserver; + + private int mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc + private static final int THROTTLE_INDEX_UNINITIALIZED = -1; + private static final int THROTTLE_INDEX_UNTHROTTLED = 0; + + private static final String PROPERTIES_FILE = "/etc/gps.conf"; + private String mNtpServer; + private boolean mNtpActive; + + public ThrottleService(Context context) { + if (DBG) Slog.d(TAG, "Starting ThrottleService"); + mContext = context; + + mNtpActive = false; + + mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + Intent pollIntent = new Intent(ACTION_POLL, null); + mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); + Intent resetIntent = new Intent(ACTION_RESET, null); + mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0); + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + mNMService = INetworkManagementService.Stub.asInterface(b); + + mNotificationManager = (NotificationManager)mContext.getSystemService( + Context.NOTIFICATION_SERVICE); + } + + private static class SettingsObserver extends ContentObserver { + private int mMsg; + private Handler mHandler; + SettingsObserver(Handler handler, int msg) { + super(handler); + mHandler = handler; + mMsg = msg; + } + + void observe(Context context) { + ContentResolver resolver = context.getContentResolver(); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.THROTTLE_POLLING_SEC), false, this); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.THROTTLE_RESET_DAY), false, this); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.THROTTLE_NOTIFICATION_TYPE), false, this); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.THROTTLE_HELP_URI), false, this); + } + + @Override + public void onChange(boolean selfChange) { + mHandler.obtainMessage(mMsg).sendToTarget(); + } + } + + private void enforceAccessPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, + "ThrottleService"); + } + + private long ntpToWallTime(long ntpTime) { + long bestNow = getBestTime(); + long localNow = System.currentTimeMillis(); + return localNow + (ntpTime - bestNow); + } + + // TODO - fetch for the iface + // return time in the local, system wall time, correcting for the use of ntp + + public synchronized long getResetTime(String iface) { + enforceAccessPermission(); + long resetTime = 0; + if (mRecorder != null) { + resetTime = ntpToWallTime(mRecorder.getPeriodEnd()); + } + return resetTime; + } + + // TODO - fetch for the iface + // return time in the local, system wall time, correcting for the use of ntp + public synchronized long getPeriodStartTime(String iface) { + enforceAccessPermission(); + long startTime = 0; + if (mRecorder != null) { + startTime = ntpToWallTime(mRecorder.getPeriodStart()); + } + return startTime; + } + //TODO - a better name? getCliffByteCountThreshold? + // TODO - fetch for the iface + public synchronized long getCliffThreshold(String iface, int cliff) { + enforceAccessPermission(); + if (cliff == 1) { + return mPolicyThreshold; + } + return 0; + } + // TODO - a better name? getThrottleRate? + // TODO - fetch for the iface + public synchronized int getCliffLevel(String iface, int cliff) { + enforceAccessPermission(); + if (cliff == 1) { + return mPolicyThrottleValue; + } + return 0; + } + + public String getHelpUri() { + enforceAccessPermission(); + return Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.THROTTLE_HELP_URI); + } + + // TODO - fetch for the iface + public synchronized long getByteCount(String iface, int dir, int period, int ago) { + enforceAccessPermission(); + if ((period == ThrottleManager.PERIOD_CYCLE) && + (mRecorder != null)) { + if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago); + if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago); + } + return 0; + } + + // TODO - a better name - getCurrentThrottleRate? + // TODO - fetch for the iface + public synchronized int getThrottle(String iface) { + enforceAccessPermission(); + if (mThrottleIndex == 1) { + return mPolicyThrottleValue; + } + return 0; + } + + void systemReady() { + if (DBG) Slog.d(TAG, "systemReady"); + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget(); + } + }, new IntentFilter(ACTION_POLL)); + + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget(); + } + }, new IntentFilter(ACTION_RESET)); + + // use a new thread as we don't want to stall the system for file writes + mThread = new HandlerThread(TAG); + mThread.start(); + mHandler = new MyHandler(mThread.getLooper()); + mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget(); + + mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED); + mSettingsObserver.observe(mContext); + + FileInputStream stream = null; + try { + Properties properties = new Properties(); + File file = new File(PROPERTIES_FILE); + stream = new FileInputStream(file); + properties.load(stream); + mNtpServer = properties.getProperty("NTP_SERVER", null); + } catch (IOException e) { + Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (Exception e) {} + } + } + } + + + private static final int EVENT_REBOOT_RECOVERY = 0; + private static final int EVENT_POLICY_CHANGED = 1; + private static final int EVENT_POLL_ALARM = 2; + private static final int EVENT_RESET_ALARM = 3; + private class MyHandler extends Handler { + public MyHandler(Looper l) { + super(l); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_REBOOT_RECOVERY: + onRebootRecovery(); + break; + case EVENT_POLICY_CHANGED: + onPolicyChanged(); + break; + case EVENT_POLL_ALARM: + onPollAlarm(); + break; + case EVENT_RESET_ALARM: + onResetAlarm(); + } + } + + private void onRebootRecovery() { + if (DBG) Slog.d(TAG, "onRebootRecovery"); + // check for sim change TODO + // reregister for notification of policy change + + mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED; + + mRecorder = new DataRecorder(mContext, ThrottleService.this); + + // get policy + mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget(); + + // if we poll now we won't have network connectivity or even imsi access + // queue up a poll to happen in a little while - after ntp and imsi are avail + // TODO - make this callback based (ie, listen for notificaitons) + mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_POLL_ALARM), + INITIAL_POLL_DELAY_SEC * 1000); + } + + // check for new policy info (threshold limit/value/etc) + private void onPolicyChanged() { + boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true"); + + int pollingPeriod = mContext.getResources().getInteger( + R.integer.config_datause_polling_period_sec); + mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod); + + // TODO - remove testing stuff? + long defaultThreshold = mContext.getResources().getInteger( + R.integer.config_datause_threshold_bytes); + int defaultValue = mContext.getResources().getInteger( + R.integer.config_datause_throttle_kbitsps); + synchronized (ThrottleService.this) { + mPolicyThreshold = Settings.Secure.getLong(mContext.getContentResolver(), + Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold); + mPolicyThrottleValue = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue); + if (testing) { + mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC; + mPolicyThreshold = TESTING_THRESHOLD; + } + } + + mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.THROTTLE_RESET_DAY, -1); + if (mPolicyResetDay == -1 || + ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) { + Random g = new Random(); + mPolicyResetDay = 1 + g.nextInt(28); // 1-28 + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay); + } + mIface = mContext.getResources().getString(R.string.config_datause_iface); + synchronized (ThrottleService.this) { + if (mIface == null) { + mPolicyThreshold = 0; + } + } + + int defaultNotificationType = mContext.getResources().getInteger( + R.integer.config_datause_notification_type); + mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType); + + Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" + mPolicyPollPeriodSec + + ", threshold=" + mPolicyThreshold + ", value=" + mPolicyThrottleValue + + ", resetDay=" + mPolicyResetDay + ", noteType=" + + mPolicyNotificationsAllowedMask); + + // force updates + mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED; + + onResetAlarm(); + + onPollAlarm(); + + Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION); + mContext.sendBroadcast(broadcast); + } + + private void onPollAlarm() { + long now = SystemClock.elapsedRealtime(); + long next = now + mPolicyPollPeriodSec*1000; + + checkForAuthoritativeTime(); + + long incRead = 0; + long incWrite = 0; + try { + incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead; + incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite; + // handle iface resets - on some device the 3g iface comes and goes and gets + // totals reset to 0. Deal with it + if ((incRead < 0) || (incWrite < 0)) { + incRead += mLastRead; + incWrite += mLastWrite; + mLastRead = 0; + mLastWrite = 0; + } + } catch (RemoteException e) { + Slog.e(TAG, "got remoteException in onPollAlarm:" + e); + } + // don't count this data if we're roaming. + boolean roaming = "true".equals( + SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING)); + if (!roaming) { + mRecorder.addData(incRead, incWrite); + } + + long periodRx = mRecorder.getPeriodRx(0); + long periodTx = mRecorder.getPeriodTx(0); + long total = periodRx + periodTx; + if (DBG) { + Slog.d(TAG, "onPollAlarm - roaming =" + roaming + + ", read =" + incRead + ", written =" + incWrite + ", new total =" + total); + } + mLastRead += incRead; + mLastWrite += incWrite; + + checkThrottleAndPostNotification(total); + + Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION); + broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx); + broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx); + broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface)); + broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface)); + mContext.sendStickyBroadcast(broadcast); + + mAlarmManager.cancel(mPendingPollIntent); + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, next, mPendingPollIntent); + } + + private void checkThrottleAndPostNotification(long currentTotal) { + // is throttling enabled? + if (mPolicyThreshold == 0) { + clearThrottleAndNotification(); + return; + } + + // have we spoken with an ntp server yet? + // this is controversial, but we'd rather err towards not throttling + if ((mNtpServer != null) && !mNtpActive) { + return; + } + + // check if we need to throttle + if (currentTotal > mPolicyThreshold) { + if (mThrottleIndex != 1) { + synchronized (ThrottleService.this) { + mThrottleIndex = 1; + } + if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!"); + try { + mNMService.setInterfaceThrottle(mIface, + mPolicyThrottleValue, mPolicyThrottleValue); + } catch (Exception e) { + Slog.e(TAG, "error setting Throttle: " + e); + } + + mNotificationManager.cancel(R.drawable.stat_sys_throttled); + + postNotification(R.string.throttled_notification_title, + R.string.throttled_notification_message, + R.drawable.stat_sys_throttled, + Notification.FLAG_ONGOING_EVENT); + + Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION); + broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, mPolicyThrottleValue); + mContext.sendStickyBroadcast(broadcast); + + } // else already up! + } else { + clearThrottleAndNotification(); + if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) { + // check if we should warn about throttle + // pretend we only have 1/2 the time remaining that we actually do + // if our burn rate in the period so far would have us exceed the limit + // in that 1/2 window, warn the user. + // this gets more generous in the early to middle period and converges back + // to the limit as we move toward the period end. + + // adding another factor - it must be greater than the total cap/4 + // else we may get false alarms very early in the period.. in the first + // tenth of a percent of the period if we used more than a tenth of a percent + // of the cap we'd get a warning and that's not desired. + long start = mRecorder.getPeriodStart(); + long end = mRecorder.getPeriodEnd(); + long periodLength = end - start; + long now = System.currentTimeMillis(); + long timeUsed = now - start; + long warningThreshold = 2*mPolicyThreshold*timeUsed/(timeUsed+periodLength); + if ((currentTotal > warningThreshold) && (currentTotal > mPolicyThreshold/4)) { + if (mWarningNotificationSent == false) { + mWarningNotificationSent = true; + mNotificationManager.cancel(R.drawable.stat_sys_throttled); + postNotification(R.string.throttle_warning_notification_title, + R.string.throttle_warning_notification_message, + R.drawable.stat_sys_throttled, + 0); + } + } else { + if (mWarningNotificationSent == true) { + mNotificationManager.cancel(R.drawable.stat_sys_throttled); + mWarningNotificationSent =false; + } + } + } + } + } + + private void postNotification(int titleInt, int messageInt, int icon, int flags) { + Intent intent = new Intent(); + // TODO - fix up intent + intent.setClassName("com.android.phone", "com.android.phone.DataUsage"); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + Resources r = Resources.getSystem(); + CharSequence title = r.getText(titleInt); + CharSequence message = r.getText(messageInt); + if (mThrottlingNotification == null) { + mThrottlingNotification = new Notification(); + mThrottlingNotification.when = 0; + // TODO - fixup icon + mThrottlingNotification.icon = icon; + mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND; + } + mThrottlingNotification.flags = flags; + mThrottlingNotification.tickerText = title; + mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi); + + mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification); + } + + + private synchronized void clearThrottleAndNotification() { + if (mThrottleIndex != THROTTLE_INDEX_UNTHROTTLED) { + synchronized (ThrottleService.this) { + mThrottleIndex = THROTTLE_INDEX_UNTHROTTLED; + } + try { + mNMService.setInterfaceThrottle(mIface, -1, -1); + } catch (Exception e) { + Slog.e(TAG, "error clearing Throttle: " + e); + } + Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION); + broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1); + mContext.sendStickyBroadcast(broadcast); + mNotificationManager.cancel(R.drawable.stat_sys_throttled); + mWarningNotificationSent = false; + } + } + + private Calendar calculatePeriodEnd(long now) { + Calendar end = GregorianCalendar.getInstance(); + end.setTimeInMillis(now); + int day = end.get(Calendar.DAY_OF_MONTH); + end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay); + end.set(Calendar.HOUR_OF_DAY, 0); + end.set(Calendar.MINUTE, 0); + end.set(Calendar.SECOND, 0); + end.set(Calendar.MILLISECOND, 0); + if (day >= mPolicyResetDay) { + int month = end.get(Calendar.MONTH); + if (month == Calendar.DECEMBER) { + end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1); + month = Calendar.JANUARY - 1; + } + end.set(Calendar.MONTH, month + 1); + } + + // TODO - remove! + if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) { + end = GregorianCalendar.getInstance(); + end.setTimeInMillis(now); + end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC); + } + return end; + } + private Calendar calculatePeriodStart(Calendar end) { + Calendar start = (Calendar)end.clone(); + int month = end.get(Calendar.MONTH); + if (end.get(Calendar.MONTH) == Calendar.JANUARY) { + month = Calendar.DECEMBER + 1; + start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1); + } + start.set(Calendar.MONTH, month - 1); + + // TODO - remove!! + if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) { + start = (Calendar)end.clone(); + start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC); + } + return start; + } + + private void onResetAlarm() { + if (DBG) { + Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) + + " bytes read and " + mRecorder.getPeriodTx(0) + " written"); + } + + long now = getBestTime(); + + if (mNtpActive || (mNtpServer == null)) { + Calendar end = calculatePeriodEnd(now); + Calendar start = calculatePeriodStart(end); + + if (mRecorder.setNextPeriod(start, end)) { + onPollAlarm(); + } + + mAlarmManager.cancel(mPendingResetIntent); + long offset = end.getTimeInMillis() - now; + // use Elapsed realtime so clock changes don't fool us. + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + offset, + mPendingResetIntent); + } else { + if (DBG) Slog.d(TAG, "no authoritative time - not resetting period"); + } + } + } + + private void checkForAuthoritativeTime() { + if (mNtpActive || (mNtpServer == null)) return; + + // will try to get the ntp time and switch to it if found. + // will also cache the time so we don't fetch it repeatedly. + getBestTime(); + } + + private static final int MAX_NTP_CACHE_AGE = 30 * 1000; + private static final int MAX_NTP_FETCH_WAIT = 10 * 1000; + private long cachedNtp; + private long cachedNtpTimestamp; + + private long getBestTime() { + if (mNtpServer != null) { + if (mNtpActive) { + long ntpAge = SystemClock.elapsedRealtime() - cachedNtpTimestamp; + if (ntpAge < MAX_NTP_CACHE_AGE) { + if (VDBG) Slog.v(TAG, "using cached time"); + return cachedNtp + ntpAge; + } + } + SntpClient client = new SntpClient(); + if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT)) { + cachedNtp = client.getNtpTime(); + cachedNtpTimestamp = SystemClock.elapsedRealtime(); + if (!mNtpActive) { + mNtpActive = true; + if (DBG) Slog.d(TAG, "found Authoritative time - reseting alarm"); + mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget(); + } + if (VDBG) Slog.v(TAG, "using Authoritative time: " + cachedNtp); + return cachedNtp; + } + } + long time = System.currentTimeMillis(); + if (VDBG) Slog.v(TAG, "using User time: " + time); + mNtpActive = false; + return time; + } + + // records bytecount data for a given time and accumulates it into larger time windows + // for logging and other purposes + // + // since time can be changed (user or network action) we will have to track the time of the + // last recording and deal with it. + private static class DataRecorder { + long[] mPeriodRxData; + long[] mPeriodTxData; + int mCurrentPeriod; + int mPeriodCount; + + Calendar mPeriodStart; + Calendar mPeriodEnd; + + ThrottleService mParent; + Context mContext; + String mImsi = null; + + TelephonyManager mTelephonyManager; + + DataRecorder(Context context, ThrottleService parent) { + mContext = context; + mParent = parent; + + mTelephonyManager = (TelephonyManager)mContext.getSystemService( + Context.TELEPHONY_SERVICE); + + synchronized (mParent) { + mPeriodCount = 6; + mPeriodRxData = new long[mPeriodCount]; + mPeriodTxData = new long[mPeriodCount]; + + mPeriodStart = Calendar.getInstance(); + mPeriodEnd = Calendar.getInstance(); + + retrieve(); + } + } + + boolean setNextPeriod(Calendar start, Calendar end) { + // TODO - how would we deal with a dual-IMSI device? + checkForSubscriberId(); + boolean startNewPeriod = true; + + if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) { + // same endpoints - keep collecting + if (DBG) { + Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," + + end.getTimeInMillis() +") - ammending data"); + } + startNewPeriod = false; + } else { + if (DBG) { + if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) { + Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," + + end.getTimeInMillis() + ") - old end was " + + mPeriodEnd.getTimeInMillis() + ", following"); + } else { + Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," + + end.getTimeInMillis() + ") replacing old (" + + mPeriodStart.getTimeInMillis() + "," + + mPeriodEnd.getTimeInMillis() + ")"); + } + } + synchronized (mParent) { + ++mCurrentPeriod; + if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0; + mPeriodRxData[mCurrentPeriod] = 0; + mPeriodTxData[mCurrentPeriod] = 0; + } + } + setPeriodStart(start); + setPeriodEnd(end); + record(); + return startNewPeriod; + } + + public long getPeriodEnd() { + synchronized (mParent) { + return mPeriodEnd.getTimeInMillis(); + } + } + + private void setPeriodEnd(Calendar end) { + synchronized (mParent) { + mPeriodEnd = end; + } + } + + public long getPeriodStart() { + synchronized (mParent) { + return mPeriodStart.getTimeInMillis(); + } + } + + private void setPeriodStart(Calendar start) { + synchronized (mParent) { + mPeriodStart = start; + } + } + + public int getPeriodCount() { + synchronized (mParent) { + return mPeriodCount; + } + } + + private void zeroData(int field) { + synchronized (mParent) { + for(int period = 0; period<mPeriodCount; period++) { + mPeriodRxData[period] = 0; + mPeriodTxData[period] = 0; + } + mCurrentPeriod = 0; + } + + } + + // if time moves backward accumulate all read/write that's lost into the now + // otherwise time moved forward. + void addData(long bytesRead, long bytesWritten) { + checkForSubscriberId(); + + synchronized (mParent) { + mPeriodRxData[mCurrentPeriod] += bytesRead; + mPeriodTxData[mCurrentPeriod] += bytesWritten; + } + record(); + } + + private File getDataFile() { + File dataDir = Environment.getDataDirectory(); + File throttleDir = new File(dataDir, "system/throttle"); + throttleDir.mkdirs(); + String mImsi = mTelephonyManager.getSubscriberId(); + File dataFile; + if (mImsi == null) { + dataFile = useMRUFile(throttleDir); + if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile); + } else { + String imsiHash = Integer.toString(mImsi.hashCode()); + dataFile = new File(throttleDir, imsiHash); + } + // touch the file so it's not LRU + dataFile.setLastModified(System.currentTimeMillis()); + checkAndDeleteLRUDataFile(throttleDir); + return dataFile; + } + + // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling + private void checkForSubscriberId() { + if (mImsi != null) return; + + mImsi = mTelephonyManager.getSubscriberId(); + if (mImsi == null) return; + + if (DBG) Slog.d(TAG, "finally have imsi - retreiving data"); + retrieve(); + } + + private final static int MAX_SIMS_SUPPORTED = 3; + + private void checkAndDeleteLRUDataFile(File dir) { + File[] files = dir.listFiles(); + + if (files.length <= MAX_SIMS_SUPPORTED) return; + if (DBG) Slog.d(TAG, "Too many data files"); + do { + File oldest = null; + for (File f : files) { + if ((oldest == null) || (oldest.lastModified() > f.lastModified())) { + oldest = f; + } + } + if (oldest == null) return; + if (DBG) Slog.d(TAG, " deleting " + oldest); + oldest.delete(); + files = dir.listFiles(); + } while (files.length > MAX_SIMS_SUPPORTED); + } + + private File useMRUFile(File dir) { + File newest = null; + File[] files = dir.listFiles(); + + for (File f : files) { + if ((newest == null) || (newest.lastModified() < f.lastModified())) { + newest = f; + } + } + if (newest == null) { + newest = new File(dir, "temp"); + } + return newest; + } + + + private static final int DATA_FILE_VERSION = 1; + + private void record() { + // 1 int version + // 1 int mPeriodCount + // 13*6 long[PERIOD_COUNT] mPeriodRxData + // 13*6 long[PERIOD_COUNT] mPeriodTxData + // 1 int mCurrentPeriod + // 13 long periodStartMS + // 13 long periodEndMS + // 200 chars max + StringBuilder builder = new StringBuilder(); + builder.append(DATA_FILE_VERSION); + builder.append(":"); + builder.append(mPeriodCount); + builder.append(":"); + for(int i = 0; i < mPeriodCount; i++) { + builder.append(mPeriodRxData[i]); + builder.append(":"); + } + for(int i = 0; i < mPeriodCount; i++) { + builder.append(mPeriodTxData[i]); + builder.append(":"); + } + builder.append(mCurrentPeriod); + builder.append(":"); + builder.append(mPeriodStart.getTimeInMillis()); + builder.append(":"); + builder.append(mPeriodEnd.getTimeInMillis()); + + BufferedWriter out = null; + try { + out = new BufferedWriter(new FileWriter(getDataFile()), 256); + out.write(builder.toString()); + } catch (IOException e) { + Slog.e(TAG, "Error writing data file"); + return; + } finally { + if (out != null) { + try { + out.close(); + } catch (Exception e) {} + } + } + } + + private void retrieve() { + // clean out any old data first. If we fail to read we don't want old stuff + zeroData(0); + + File f = getDataFile(); + byte[] buffer; + FileInputStream s = null; + try { + buffer = new byte[(int)f.length()]; + s = new FileInputStream(f); + s.read(buffer); + } catch (IOException e) { + Slog.e(TAG, "Error reading data file"); + return; + } finally { + if (s != null) { + try { + s.close(); + } catch (Exception e) {} + } + } + String data = new String(buffer); + if (data == null || data.length() == 0) { + if (DBG) Slog.d(TAG, "data file empty"); + return; + } + synchronized (mParent) { + String[] parsed = data.split(":"); + int parsedUsed = 0; + if (parsed.length < 6) { + Slog.e(TAG, "reading data file with insufficient length - ignoring"); + return; + } + + if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) { + Slog.e(TAG, "reading data file with bad version - ignoring"); + return; + } + + mPeriodCount = Integer.parseInt(parsed[parsedUsed++]); + if (parsed.length != 5 + (2 * mPeriodCount)) { + Slog.e(TAG, "reading data file with bad length ("+parsed.length+" != "+(5 + (2*mPeriodCount))+") - ignoring"); + return; + } + + mPeriodRxData = new long[mPeriodCount]; + for(int i = 0; i < mPeriodCount; i++) { + mPeriodRxData[i] = Long.parseLong(parsed[parsedUsed++]); + } + mPeriodTxData = new long[mPeriodCount]; + for(int i = 0; i < mPeriodCount; i++) { + mPeriodTxData[i] = Long.parseLong(parsed[parsedUsed++]); + } + mCurrentPeriod = Integer.parseInt(parsed[parsedUsed++]); + mPeriodStart = new GregorianCalendar(); + mPeriodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++])); + mPeriodEnd = new GregorianCalendar(); + mPeriodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++])); + } + } + + long getPeriodRx(int which) { + synchronized (mParent) { + if (which > mPeriodCount) return 0; + which = mCurrentPeriod - which; + if (which < 0) which += mPeriodCount; + return mPeriodRxData[which]; + } + } + long getPeriodTx(int which) { + synchronized (mParent) { + if (which > mPeriodCount) return 0; + which = mCurrentPeriod - which; + if (which < 0) which += mPeriodCount; + return mPeriodTxData[which]; + } + } + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump ThrottleService " + + "from from pid=" + Binder.getCallingPid() + ", uid=" + + Binder.getCallingUid()); + return; + } + pw.println(); + + pw.println("The threshold is " + mPolicyThreshold + + ", after which you experince throttling to " + + mPolicyThrottleValue + "kbps"); + pw.println("Current period is " + + (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " + + "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 + + " seconds."); + pw.println("Polling every " + mPolicyPollPeriodSec + " seconds"); + pw.println("Current Throttle Index is " + mThrottleIndex); + + for (int i = 0; i < mRecorder.getPeriodCount(); i++) { + pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" + + mRecorder.getPeriodTx(i)); + } + } +} diff --git a/services/java/com/android/server/TwilightCalculator.java b/services/java/com/android/server/TwilightCalculator.java new file mode 100644 index 0000000..a5c93b5 --- /dev/null +++ b/services/java/com/android/server/TwilightCalculator.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.text.format.DateUtils; +import android.util.FloatMath; + +/** @hide */ +public class TwilightCalculator { + + /** Value of {@link #mState} if it is currently day */ + public static final int DAY = 0; + + /** Value of {@link #mState} if it is currently night */ + public static final int NIGHT = 1; + + private static final float DEGREES_TO_RADIANS = (float) (Math.PI / 180.0f); + + // element for calculating solar transit. + private static final float J0 = 0.0009f; + + // correction for civil twilight + private static final float ALTIDUTE_CORRECTION_CIVIL_TWILIGHT = -0.104719755f; + + // coefficients for calculating Equation of Center. + private static final float C1 = 0.0334196f; + private static final float C2 = 0.000349066f; + private static final float C3 = 0.000005236f; + + private static final float OBLIQUITY = 0.40927971f; + + // Java time on Jan 1, 2000 12:00 UTC. + private static final long UTC_2000 = 946728000000L; + + /** + * Time of sunset (civil twilight) in milliseconds or -1 in the case the day + * or night never ends. + */ + public long mSunset; + + /** + * Time of sunrise (civil twilight) in milliseconds or -1 in the case the + * day or night never ends. + */ + public long mSunrise; + + /** Current state */ + public int mState; + + /** + * calculates the civil twilight bases on time and geo-coordinates. + * + * @param time time in milliseconds. + * @param latiude latitude in degrees. + * @param longitude latitude in degrees. + */ + public void calculateTwilight(long time, double latiude, double longitude) { + final float daysSince2000 = (float) (time - UTC_2000) / DateUtils.DAY_IN_MILLIS; + + // mean anomaly + final float meanAnomaly = 6.240059968f + daysSince2000 * 0.01720197f; + + // true anomaly + final float trueAnomaly = meanAnomaly + C1 * FloatMath.sin(meanAnomaly) + C2 + * FloatMath.sin(2 * meanAnomaly) + C3 * FloatMath.sin(3 * meanAnomaly); + + // ecliptic longitude + final float solarLng = trueAnomaly + 1.796593063f + (float) Math.PI; + + // solar transit in days since 2000 + final double arcLongitude = -longitude / 360; + float n = Math.round(daysSince2000 - J0 - arcLongitude); + double solarTransitJ2000 = n + J0 + arcLongitude + 0.0053f * FloatMath.sin(meanAnomaly) + + -0.0069f * FloatMath.sin(2 * solarLng); + + // declination of sun + double solarDec = Math.asin(FloatMath.sin(solarLng) * FloatMath.sin(OBLIQUITY)); + + final double latRad = latiude * DEGREES_TO_RADIANS; + + double cosHourAngle = (FloatMath.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad) + * Math.sin(solarDec)) / (Math.cos(latRad) * Math.cos(solarDec)); + // The day or night never ends for the given date and location, if this value is out of + // range. + if (cosHourAngle >= 1) { + mState = NIGHT; + mSunset = -1; + mSunrise = -1; + return; + } else if (cosHourAngle <= -1) { + mState = DAY; + mSunset = -1; + mSunrise = -1; + return; + } + + float hourAngle = (float) (Math.acos(cosHourAngle) / (2 * Math.PI)); + + mSunset = Math.round((solarTransitJ2000 + hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000; + mSunrise = Math.round((solarTransitJ2000 - hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000; + + if (mSunrise < time && mSunset > time) { + mState = DAY; + } else { + mState = NIGHT; + } + } + +} diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java new file mode 100644 index 0000000..3606629 --- /dev/null +++ b/services/java/com/android/server/UiModeManagerService.java @@ -0,0 +1,772 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.Activity; +import android.app.ActivityManagerNative; +import android.app.AlarmManager; +import android.app.IUiModeManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.StatusBarManager; +import android.app.UiModeManager; +import android.content.ActivityNotFoundException; +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.Configuration; +import android.location.Criteria; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.BatteryManager; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Slog; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Iterator; + +import com.android.internal.R; +import com.android.internal.app.DisableCarModeActivity; + +class UiModeManagerService extends IUiModeManager.Stub { + private static final String TAG = UiModeManager.class.getSimpleName(); + private static final boolean LOG = false; + + private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL"; + + private static final int MSG_UPDATE_TWILIGHT = 0; + private static final int MSG_ENABLE_LOCATION_UPDATES = 1; + + private static final long LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS; + private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20; + private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000; + private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 5 * DateUtils.MINUTE_IN_MILLIS; + private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS; + + private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE"; + + private final Context mContext; + + final Object mLock = new Object(); + + private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + + private int mNightMode = UiModeManager.MODE_NIGHT_NO; + private boolean mCarModeEnabled = false; + private boolean mCharging = false; + private final boolean mCarModeKeepsScreenOn; + private final boolean mDeskModeKeepsScreenOn; + + private boolean mComputedNightMode; + private int mCurUiMode = 0; + private int mSetUiMode = 0; + + private boolean mHoldingConfiguration = false; + private Configuration mConfiguration = new Configuration(); + + private boolean mSystemReady; + + private NotificationManager mNotificationManager; + + private AlarmManager mAlarmManager; + + private LocationManager mLocationManager; + private Location mLocation; + private StatusBarManager mStatusBarManager; + private final PowerManager.WakeLock mWakeLock; + + static Intent buildHomeIntent(String category) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(category); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + return intent; + } + + // The broadcast receiver which receives the result of the ordered broadcast sent when + // the dock state changes. The original ordered broadcast is sent with an initial result + // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g., + // to RESULT_CANCELED, then the intent to start a dock app will not be sent. + private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (getResultCode() != Activity.RESULT_OK) { + return; + } + + final int enableFlags = intent.getIntExtra("enableFlags", 0); + final int disableFlags = intent.getIntExtra("disableFlags", 0); + + synchronized (mLock) { + // Launch a dock activity + String category = null; + if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) { + // Only launch car home when car mode is enabled and the caller + // has asked us to switch to it. + if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + category = Intent.CATEGORY_CAR_DOCK; + } + } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) { + // Only launch car home when desk mode is enabled and the caller + // has asked us to switch to it. Currently re-using the car + // mode flag since we don't have a formal API for "desk mode". + if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + category = Intent.CATEGORY_DESK_DOCK; + } + } else { + // Launch the standard home app if requested. + if ((disableFlags&UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { + category = Intent.CATEGORY_HOME; + } + } + + if (category != null) { + // This is the new activity that will serve as home while + // we are in care mode. + Intent homeIntent = buildHomeIntent(category); + + // Now we are going to be careful about switching the + // configuration and starting the activity -- we need to + // do this in a specific order under control of the + // activity manager, to do it cleanly. So compute the + // new config, but don't set it yet, and let the + // activity manager take care of both the start and config + // change. + Configuration newConfig = null; + if (mHoldingConfiguration) { + mHoldingConfiguration = false; + updateConfigurationLocked(false); + newConfig = mConfiguration; + } + try { + ActivityManagerNative.getDefault().startActivityWithConfig( + null, homeIntent, null, null, 0, null, null, 0, false, false, + newConfig); + mHoldingConfiguration = false; + } catch (RemoteException e) { + Slog.w(TAG, e.getCause()); + } + } + + if (mHoldingConfiguration) { + mHoldingConfiguration = false; + updateConfigurationLocked(true); + } + } + } + }; + + private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { + mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT); + } + } + }; + + private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + updateDockState(state); + } + }; + + private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mCharging = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0); + synchronized (mLock) { + if (mSystemReady) { + updateLocked(0, 0); + } + } + } + }; + + // A LocationListener to initialize the network location provider. The location updates + // are handled through the passive location provider. + private final LocationListener mEmptyLocationListener = new LocationListener() { + public void onLocationChanged(Location location) { + } + + public void onProviderDisabled(String provider) { + } + + public void onProviderEnabled(String provider) { + } + + public void onStatusChanged(String provider, int status, Bundle extras) { + } + }; + + private final LocationListener mLocationListener = new LocationListener() { + + public void onLocationChanged(Location location) { + final boolean hasMoved = hasMoved(location); + final boolean hasBetterAccuracy = mLocation == null + || location.getAccuracy() < mLocation.getAccuracy(); + if (hasMoved || hasBetterAccuracy) { + synchronized (mLock) { + mLocation = location; + if (hasMoved && isDoingNightMode() + && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { + mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT); + } + } + } + } + + public void onProviderDisabled(String provider) { + } + + public void onProviderEnabled(String provider) { + } + + public void onStatusChanged(String provider, int status, Bundle extras) { + } + + /* + * The user has moved if the accuracy circles of the two locations + * don't overlap. + */ + private boolean hasMoved(Location location) { + if (location == null) { + return false; + } + if (mLocation == null) { + return true; + } + + /* if new location is older than the current one, the devices hasn't + * moved. + */ + if (location.getTime() < mLocation.getTime()) { + return false; + } + + /* Get the distance between the two points */ + float distance = mLocation.distanceTo(location); + + /* Get the total accuracy radius for both locations */ + float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy(); + + /* If the distance is greater than the combined accuracy of the two + * points then they can't overlap and hence the user has moved. + */ + return distance >= totalAccuracy; + } + }; + + public UiModeManagerService(Context context) { + mContext = context; + + ServiceManager.addService(Context.UI_MODE_SERVICE, this); + + mAlarmManager = + (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + mLocationManager = + (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE); + mContext.registerReceiver(mTwilightUpdateReceiver, + new IntentFilter(ACTION_UPDATE_NIGHT_MODE)); + mContext.registerReceiver(mDockModeReceiver, + new IntentFilter(Intent.ACTION_DOCK_EVENT)); + mContext.registerReceiver(mBatteryReceiver, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + + PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); + + mConfiguration.setToDefaults(); + + mCarModeKeepsScreenOn = (context.getResources().getInteger( + com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1); + mDeskModeKeepsScreenOn = (context.getResources().getInteger( + com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1); + + mNightMode = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO); + } + + public void disableCarMode(int flags) { + synchronized (mLock) { + setCarModeLocked(false); + if (mSystemReady) { + updateLocked(0, flags); + } + } + } + + public void enableCarMode(int flags) { + synchronized (mLock) { + setCarModeLocked(true); + if (mSystemReady) { + updateLocked(flags, 0); + } + } + } + + public int getCurrentModeType() { + synchronized (mLock) { + return mCurUiMode & Configuration.UI_MODE_TYPE_MASK; + } + } + + public void setNightMode(int mode) throws RemoteException { + synchronized (mLock) { + switch (mode) { + case UiModeManager.MODE_NIGHT_NO: + case UiModeManager.MODE_NIGHT_YES: + case UiModeManager.MODE_NIGHT_AUTO: + break; + default: + throw new IllegalArgumentException("Unknown mode: " + mode); + } + if (!isDoingNightMode()) { + return; + } + + if (mNightMode != mode) { + long ident = Binder.clearCallingIdentity(); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.UI_NIGHT_MODE, mode); + Binder.restoreCallingIdentity(ident); + mNightMode = mode; + updateLocked(0, 0); + } + } + } + + public int getNightMode() throws RemoteException { + return mNightMode; + } + + void systemReady() { + synchronized (mLock) { + mSystemReady = true; + mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR; + updateLocked(0, 0); + mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES); + } + } + + boolean isDoingNightMode() { + return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED; + } + + void setCarModeLocked(boolean enabled) { + if (mCarModeEnabled != enabled) { + mCarModeEnabled = enabled; + } + } + + void updateDockState(int newState) { + synchronized (mLock) { + if (newState != mDockState) { + mDockState = newState; + setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR); + if (mSystemReady) { + updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0); + } + } + } + } + + final void updateConfigurationLocked(boolean sendIt) { + int uiMode = Configuration.UI_MODE_TYPE_NORMAL; + if (mCarModeEnabled) { + uiMode = Configuration.UI_MODE_TYPE_CAR; + } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { + uiMode = Configuration.UI_MODE_TYPE_DESK; + } + if (mCarModeEnabled) { + if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) { + updateTwilightLocked(); + uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES + : Configuration.UI_MODE_NIGHT_NO; + } else { + uiMode |= mNightMode << 4; + } + } else { + // Disabling the car mode clears the night mode. + uiMode = (uiMode & ~Configuration.UI_MODE_NIGHT_MASK) | Configuration.UI_MODE_NIGHT_NO; + } + + if (LOG) { + Slog.d(TAG, + "updateConfigurationLocked: mDockState=" + mDockState + + "; mCarMode=" + mCarModeEnabled + + "; mNightMode=" + mNightMode + + "; uiMode=" + uiMode); + } + + mCurUiMode = uiMode; + + if (!mHoldingConfiguration && uiMode != mSetUiMode) { + mSetUiMode = uiMode; + mConfiguration.uiMode = uiMode; + + if (sendIt) { + try { + ActivityManagerNative.getDefault().updateConfiguration(mConfiguration); + } catch (RemoteException e) { + Slog.w(TAG, "Failure communicating with activity manager", e); + } + } + } + } + + final void updateLocked(int enableFlags, int disableFlags) { + long ident = Binder.clearCallingIdentity(); + + try { + String action = null; + String oldAction = null; + if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) { + adjustStatusBarCarModeLocked(); + oldAction = UiModeManager.ACTION_EXIT_CAR_MODE; + } else if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_DESK) { + oldAction = UiModeManager.ACTION_EXIT_DESK_MODE; + } + + if (mCarModeEnabled) { + if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) { + adjustStatusBarCarModeLocked(); + + if (oldAction != null) { + mContext.sendBroadcast(new Intent(oldAction)); + } + mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR; + action = UiModeManager.ACTION_ENTER_CAR_MODE; + } + } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { + if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_DESK) { + if (oldAction != null) { + mContext.sendBroadcast(new Intent(oldAction)); + } + mLastBroadcastState = Intent.EXTRA_DOCK_STATE_DESK; + action = UiModeManager.ACTION_ENTER_DESK_MODE; + } + } else { + mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + action = oldAction; + } + + if (action != null) { + // Send the ordered broadcast; the result receiver will receive after all + // broadcasts have been sent. If any broadcast receiver changes the result + // code from the initial value of RESULT_OK, then the result receiver will + // not launch the corresponding dock application. This gives apps a chance + // to override the behavior and stay in their app even when the device is + // placed into a dock. + Intent intent = new Intent(action); + intent.putExtra("enableFlags", enableFlags); + intent.putExtra("disableFlags", disableFlags); + mContext.sendOrderedBroadcast(intent, null, + mResultReceiver, null, Activity.RESULT_OK, null, null); + // Attempting to make this transition a little more clean, we are going + // to hold off on doing a configuration change until we have finished + // the broadcast and started the home activity. + mHoldingConfiguration = true; + } else { + Intent homeIntent = null; + if (mCarModeEnabled) { + if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + homeIntent = buildHomeIntent(Intent.CATEGORY_CAR_DOCK); + } + } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { + if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + homeIntent = buildHomeIntent(Intent.CATEGORY_DESK_DOCK); + } + } else { + if ((disableFlags&UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { + homeIntent = buildHomeIntent(Intent.CATEGORY_HOME); + } + } + if (homeIntent != null) { + try { + mContext.startActivity(homeIntent); + } catch (ActivityNotFoundException e) { + } + } + } + + updateConfigurationLocked(true); + + // keep screen on when charging and in car mode + boolean keepScreenOn = mCharging && + ((mCarModeEnabled && mCarModeKeepsScreenOn) || + (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn)); + if (keepScreenOn != mWakeLock.isHeld()) { + if (keepScreenOn) { + mWakeLock.acquire(); + } else { + mWakeLock.release(); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void adjustStatusBarCarModeLocked() { + if (mStatusBarManager == null) { + mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE); + } + + // Fear not: StatusBarService manages a list of requests to disable + // features of the status bar; these are ORed together to form the + // active disabled list. So if (for example) the device is locked and + // the status bar should be totally disabled, the calls below will + // have no effect until the device is unlocked. + if (mStatusBarManager != null) { + mStatusBarManager.disable(mCarModeEnabled + ? StatusBarManager.DISABLE_NOTIFICATION_TICKER + : StatusBarManager.DISABLE_NONE); + } + + if (mNotificationManager == null) { + mNotificationManager = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + } + + if (mNotificationManager != null) { + if (mCarModeEnabled) { + Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class); + + Notification n = new Notification(); + n.icon = R.drawable.stat_notify_car_mode; + n.defaults = Notification.DEFAULT_LIGHTS; + n.flags = Notification.FLAG_ONGOING_EVENT; + n.when = 0; + n.setLatestEventInfo( + mContext, + mContext.getString(R.string.car_mode_disable_notification_title), + mContext.getString(R.string.car_mode_disable_notification_message), + PendingIntent.getActivity(mContext, 0, carModeOffIntent, 0)); + mNotificationManager.notify(0, n); + } else { + mNotificationManager.cancel(0); + } + } + } + + private final Handler mHandler = new Handler() { + + boolean mPassiveListenerEnabled; + boolean mNetworkListenerEnabled; + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_TWILIGHT: + synchronized (mLock) { + if (isDoingNightMode() && mLocation != null + && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { + updateTwilightLocked(); + updateLocked(0, 0); + } + } + break; + case MSG_ENABLE_LOCATION_UPDATES: + // enable network provider to receive at least location updates for a given + // distance. + boolean networkLocationEnabled; + try { + networkLocationEnabled = + mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); + } catch (Exception e) { + // we may get IllegalArgumentException if network location provider + // does not exist or is not yet installed. + networkLocationEnabled = false; + } + if (!mNetworkListenerEnabled && networkLocationEnabled) { + mNetworkListenerEnabled = true; + mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, + LOCATION_UPDATE_MS, 0, mEmptyLocationListener); + + if (mLocation == null) { + retrieveLocation(); + } + synchronized (mLock) { + if (isDoingNightMode() && mLocation != null + && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { + updateTwilightLocked(); + updateLocked(0, 0); + } + } + } + // enable passive provider to receive updates from location fixes (gps + // and network). + boolean passiveLocationEnabled; + try { + passiveLocationEnabled = + mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER); + } catch (Exception e) { + // we may get IllegalArgumentException if passive location provider + // does not exist or is not yet installed. + passiveLocationEnabled = false; + } + if (!mPassiveListenerEnabled && passiveLocationEnabled) { + mPassiveListenerEnabled = true; + mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, + 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener); + } + if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) { + long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL); + interval *= 1.5; + if (interval == 0) { + interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN; + } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) { + interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX; + } + Bundle bundle = new Bundle(); + bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval); + Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES); + newMsg.setData(bundle); + mHandler.sendMessageDelayed(newMsg, interval); + } + break; + } + } + + private void retrieveLocation() { + Location location = null; + final Iterator<String> providers = + mLocationManager.getProviders(new Criteria(), true).iterator(); + while (providers.hasNext()) { + final Location lastKnownLocation = + mLocationManager.getLastKnownLocation(providers.next()); + // pick the most recent location + if (location == null || (lastKnownLocation != null && + location.getTime() < lastKnownLocation.getTime())) { + location = lastKnownLocation; + } + } + // In the case there is no location available (e.g. GPS fix or network location + // is not available yet), the longitude of the location is estimated using the timezone, + // latitude and accuracy are set to get a good average. + if (location == null) { + Time currentTime = new Time(); + currentTime.set(System.currentTimeMillis()); + double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE * + (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0)); + location = new Location("fake"); + location.setLongitude(lngOffset); + location.setLatitude(0); + location.setAccuracy(417000.0f); + location.setTime(System.currentTimeMillis()); + } + synchronized (mLock) { + mLocation = location; + } + } + }; + + void updateTwilightLocked() { + if (mLocation == null) { + return; + } + final long currentTime = System.currentTimeMillis(); + boolean nightMode; + // calculate current twilight + TwilightCalculator tw = new TwilightCalculator(); + tw.calculateTwilight(currentTime, + mLocation.getLatitude(), mLocation.getLongitude()); + if (tw.mState == TwilightCalculator.DAY) { + nightMode = false; + } else { + nightMode = true; + } + + // schedule next update + long nextUpdate = 0; + if (tw.mSunrise == -1 || tw.mSunset == -1) { + // In the case the day or night never ends the update is scheduled 12 hours later. + nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS; + } else { + final int mLastTwilightState = tw.mState; + // add some extra time to be on the save side. + nextUpdate += DateUtils.MINUTE_IN_MILLIS; + if (currentTime > tw.mSunset) { + // next update should be on the following day + tw.calculateTwilight(currentTime + + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(), + mLocation.getLongitude()); + } + + if (mLastTwilightState == TwilightCalculator.NIGHT) { + nextUpdate += tw.mSunrise; + } else { + nextUpdate += tw.mSunset; + } + } + + Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE); + PendingIntent pendingIntent = + PendingIntent.getBroadcast(mContext, 0, updateIntent, 0); + mAlarmManager.cancel(pendingIntent); + mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent); + + mComputedNightMode = nightMode; + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + + pw.println("Permission Denial: can't dump uimode service from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + synchronized (mLock) { + pw.println("Current UI Mode Service state:"); + pw.print(" mDockState="); pw.print(mDockState); + pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState); + pw.print(" mNightMode="); pw.print(mNightMode); + pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled); + pw.print(" mComputedNightMode="); pw.println(mComputedNightMode); + pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode)); + pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode)); + pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration); + pw.print(" mSystemReady="); pw.println(mSystemReady); + if (mLocation != null) { + pw.print(" mLocation="); pw.println(mLocation); + } + } + } +} diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/VibratorService.java index 88074c2..2e7e3e1 100755 --- a/services/java/com/android/server/HardwareService.java +++ b/services/java/com/android/server/VibratorService.java @@ -16,60 +16,30 @@ package com.android.server; -import com.android.internal.app.IBatteryStats; -import com.android.server.am.BatteryStatsService; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.os.Handler; -import android.os.Hardware; -import android.os.IHardwareService; -import android.os.Message; -import android.os.Power; +import android.os.IVibratorService; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.IBinder; import android.os.Binder; import android.os.SystemClock; -import android.util.Log; +import android.util.Slog; import java.util.LinkedList; import java.util.ListIterator; -public class HardwareService extends IHardwareService.Stub { - private static final String TAG = "HardwareService"; - - static final int LIGHT_ID_BACKLIGHT = 0; - static final int LIGHT_ID_KEYBOARD = 1; - static final int LIGHT_ID_BUTTONS = 2; - static final int LIGHT_ID_BATTERY = 3; - static final int LIGHT_ID_NOTIFICATIONS = 4; - static final int LIGHT_ID_ATTENTION = 5; - - static final int LIGHT_FLASH_NONE = 0; - static final int LIGHT_FLASH_TIMED = 1; - static final int LIGHT_FLASH_HARDWARE = 2; - - /** - * Light brightness is managed by a user setting. - */ - static final int BRIGHTNESS_MODE_USER = 0; - - /** - * Light brightness is managed by a light sensor. - */ - static final int BRIGHTNESS_MODE_SENSOR = 1; +public class VibratorService extends IVibratorService.Stub { + private static final String TAG = "VibratorService"; private final LinkedList<Vibration> mVibrations; private Vibration mCurrentVibration; - private boolean mAttentionLightOn; - private boolean mPulsing; - private class Vibration implements IBinder.DeathRecipient { private final IBinder mToken; private final long mTimeout; @@ -120,13 +90,11 @@ public class HardwareService extends IHardwareService.Stub { } } - HardwareService(Context context) { + VibratorService(Context context) { // Reset the hardware to a default state, in case this is a runtime // restart instead of a fresh boot. vibratorOff(); - mNativePointer = init_native(); - mContext = context; PowerManager pm = (PowerManager)context.getSystemService( Context.POWER_SERVICE); @@ -135,18 +103,11 @@ public class HardwareService extends IHardwareService.Stub { mVibrations = new LinkedList<Vibration>(); - mBatteryStats = BatteryStatsService.getService(); - IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); context.registerReceiver(mIntentReceiver, filter); } - protected void finalize() throws Throwable { - finalize_native(mNativePointer); - super.finalize(); - } - public void vibrate(long milliseconds, IBinder token) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { @@ -194,7 +155,7 @@ public class HardwareService extends IHardwareService.Stub { for (int i=0; i<N; i++) { s += " " + pattern[i]; } - Log.i(TAG, "vibrating with pattern: " + s); + Slog.i(TAG, "vibrating with pattern: " + s); } // we're running in the server so we can't fail @@ -251,92 +212,6 @@ public class HardwareService extends IHardwareService.Stub { } } - public boolean getFlashlightEnabled() { - return Hardware.getFlashlightEnabled(); - } - - public void setFlashlightEnabled(boolean on) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT) - != PackageManager.PERMISSION_GRANTED && - mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires FLASHLIGHT or HARDWARE_TEST permission"); - } - Hardware.setFlashlightEnabled(on); - } - - public void enableCameraFlash(int milliseconds) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.CAMERA) - != PackageManager.PERMISSION_GRANTED && - mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires CAMERA or HARDWARE_TEST permission"); - } - Hardware.enableCameraFlash(milliseconds); - } - - void setLightOff_UNCHECKED(int light) { - setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0, 0); - } - - void setLightBrightness_UNCHECKED(int light, int brightness, int brightnessMode) { - int b = brightness & 0x000000ff; - b = 0xff000000 | (b << 16) | (b << 8) | b; - setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0, brightnessMode); - } - - void setLightColor_UNCHECKED(int light, int color) { - setLight_native(mNativePointer, light, color, LIGHT_FLASH_NONE, 0, 0, 0); - } - - void setLightFlashing_UNCHECKED(int light, int color, int mode, int onMS, int offMS) { - setLight_native(mNativePointer, light, color, mode, onMS, offMS, 0); - } - - public void setAttentionLight(boolean on, int color) { - // Not worthy of a permission. We shouldn't have a flashlight permission. - synchronized (this) { - mAttentionLightOn = on; - mPulsing = false; - setLight_native(mNativePointer, LIGHT_ID_ATTENTION, color, - LIGHT_FLASH_HARDWARE, on ? 3 : 0, 0, 0); - } - } - - public void pulseBreathingLight() { - synchronized (this) { - // HACK: Added at the last minute of cupcake -- design this better; - // Don't reuse the attention light -- make another one. - if (false) { - Log.d(TAG, "pulseBreathingLight mAttentionLightOn=" + mAttentionLightOn - + " mPulsing=" + mPulsing); - } - if (!mAttentionLightOn && !mPulsing) { - mPulsing = true; - setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0x00ffffff, - LIGHT_FLASH_HARDWARE, 7, 0, 0); - mH.sendMessageDelayed(Message.obtain(mH, 1), 3000); - } - } - } - - private Handler mH = new Handler() { - @Override - public void handleMessage(Message msg) { - synchronized (this) { - if (false) { - Log.d(TAG, "pulse cleanup handler firing mPulsing=" + mPulsing); - } - if (mPulsing) { - mPulsing = false; - setLight_native(mNativePointer, LIGHT_ID_ATTENTION, - mAttentionLightOn ? 0xffffffff : 0, - LIGHT_FLASH_NONE, 0, 0, 0); - } - } - } - }; - private final Runnable mVibrationRunnable = new Runnable() { public void run() { synchronized (mVibrations) { @@ -452,7 +327,7 @@ public class HardwareService extends IHardwareService.Stub { // duration is saved for delay() at top of loop duration = pattern[index++]; if (duration > 0) { - HardwareService.this.vibratorOn(duration); + VibratorService.this.vibratorOn(duration); } } else { if (repeat < 0) { @@ -490,21 +365,13 @@ public class HardwareService extends IHardwareService.Stub { } }; - private static native int init_native(); - private static native void finalize_native(int ptr); - - private static native void setLight_native(int ptr, int light, int color, int mode, - int onMS, int offMS, int brightnessMode); + private Handler mH = new Handler(); private final Context mContext; private final PowerManager.WakeLock mWakeLock; - private final IBatteryStats mBatteryStats; - volatile VibrateThread mThread; - private int mNativePointer; - native static void vibratorOn(long milliseconds); native static void vibratorOff(); } diff --git a/services/java/com/android/server/ViewServer.java b/services/java/com/android/server/ViewServer.java index 07826a6..ae00438 100644 --- a/services/java/com/android/server/ViewServer.java +++ b/services/java/com/android/server/ViewServer.java @@ -16,7 +16,7 @@ package com.android.server; -import android.util.Log; +import android.util.Slog; import java.net.ServerSocket; import java.net.Socket; @@ -45,7 +45,7 @@ class ViewServer implements Runnable { private static final String LOG_TAG = "ViewServer"; private static final String VALUE_PROTOCOL_VERSION = "2"; - private static final String VALUE_SERVER_VERSION = "2"; + private static final String VALUE_SERVER_VERSION = "3"; // Protocol commands // Returns the protocol version @@ -129,7 +129,7 @@ class ViewServer implements Runnable { mServer = null; return true; } catch (IOException e) { - Log.w(LOG_TAG, "Could not close the view server"); + Slog.w(LOG_TAG, "Could not close the view server"); } } return false; @@ -191,7 +191,7 @@ class ViewServer implements Runnable { } if (!result) { - Log.w(LOG_TAG, "An error occured with the command: " + command); + Slog.w(LOG_TAG, "An error occured with the command: " + command); } } finally { if (in != null) { @@ -199,7 +199,7 @@ class ViewServer implements Runnable { } } } catch (Exception e) { - Log.w(LOG_TAG, "Connection error: ", e); + Slog.w(LOG_TAG, "Connection error: ", e); } finally { if (client != null) { try { diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 0933677..124da4e 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -23,7 +23,7 @@ import android.app.IWallpaperManager; import android.app.IWallpaperManagerCallback; import android.app.PendingIntent; import android.app.WallpaperInfo; -import android.backup.BackupManager; +import android.app.backup.BackupManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -46,7 +46,7 @@ import android.service.wallpaper.IWallpaperConnection; import android.service.wallpaper.IWallpaperEngine; import android.service.wallpaper.IWallpaperService; import android.service.wallpaper.WallpaperService; -import android.util.Log; +import android.util.Slog; import android.util.Xml; import android.view.IWindowManager; import android.view.WindowManager; @@ -65,8 +65,12 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import com.android.internal.content.PackageMonitor; import com.android.internal.service.wallpaper.ImageWallpaper; import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.JournaledFile; +import com.android.server.DevicePolicyManagerService.ActiveAdmin; +import com.android.server.DevicePolicyManagerService.MyPackageMonitor; class WallpaperManagerService extends IWallpaperManager.Stub { static final String TAG = "WallpaperService"; @@ -122,6 +126,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { final Context mContext; final IWindowManager mIWindowManager; + final MyPackageMonitor mMonitor; int mWidth = -1; int mHeight = -1; @@ -150,6 +155,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { WallpaperConnection mWallpaperConnection; long mLastDiedTime; + boolean mWallpaperUpdating; class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection { @@ -165,6 +171,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { if (mWallpaperConnection == this) { + mLastDiedTime = SystemClock.uptimeMillis(); mService = IWallpaperService.Stub.asInterface(service); attachServiceLocked(this); // XXX should probably do saveSettingsLocked() later @@ -181,10 +188,10 @@ class WallpaperManagerService extends IWallpaperManager.Stub { mService = null; mEngine = null; if (mWallpaperConnection == this) { - Log.w(TAG, "Wallpaper service gone: " + mWallpaperComponent); - if ((mLastDiedTime+MIN_WALLPAPER_CRASH_TIME) - < SystemClock.uptimeMillis()) { - Log.w(TAG, "Reverting to built-in wallpaper!"); + Slog.w(TAG, "Wallpaper service gone: " + mWallpaperComponent); + if (!mWallpaperUpdating && (mLastDiedTime+MIN_WALLPAPER_CRASH_TIME) + > SystemClock.uptimeMillis()) { + Slog.w(TAG, "Reverting to built-in wallpaper!"); bindWallpaperComponentLocked(null); } } @@ -205,11 +212,92 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } } + class MyPackageMonitor extends PackageMonitor { + @Override + public void onPackageUpdateFinished(String packageName, int uid) { + synchronized (mLock) { + if (mWallpaperComponent != null && + mWallpaperComponent.getPackageName().equals(packageName)) { + mWallpaperUpdating = false; + ComponentName comp = mWallpaperComponent; + clearWallpaperComponentLocked(); + bindWallpaperComponentLocked(comp); + } + } + } + + @Override + public void onPackageUpdateStarted(String packageName, int uid) { + synchronized (mLock) { + if (mWallpaperComponent != null && + mWallpaperComponent.getPackageName().equals(packageName)) { + mWallpaperUpdating = true; + } + } + } + + @Override + public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { + return doPackagesChanged(doit); + } + + @Override + public void onSomePackagesChanged() { + doPackagesChanged(true); + } + + boolean doPackagesChanged(boolean doit) { + boolean changed = false; + synchronized (mLock) { + if (mWallpaperComponent != null) { + int change = isPackageDisappearing(mWallpaperComponent.getPackageName()); + if (change == PACKAGE_PERMANENT_CHANGE + || change == PACKAGE_TEMPORARY_CHANGE) { + changed = true; + if (doit) { + Slog.w(TAG, "Wallpaper uninstalled, removing: " + mWallpaperComponent); + clearWallpaperLocked(); + } + } + } + if (mNextWallpaperComponent != null) { + int change = isPackageDisappearing(mNextWallpaperComponent.getPackageName()); + if (change == PACKAGE_PERMANENT_CHANGE + || change == PACKAGE_TEMPORARY_CHANGE) { + mNextWallpaperComponent = null; + } + } + if (mWallpaperComponent != null + && isPackageModified(mWallpaperComponent.getPackageName())) { + try { + mContext.getPackageManager().getServiceInfo( + mWallpaperComponent, 0); + } catch (NameNotFoundException e) { + Slog.w(TAG, "Wallpaper component gone, removing: " + mWallpaperComponent); + clearWallpaperLocked(); + } + } + if (mNextWallpaperComponent != null + && isPackageModified(mNextWallpaperComponent.getPackageName())) { + try { + mContext.getPackageManager().getServiceInfo( + mNextWallpaperComponent, 0); + } catch (NameNotFoundException e) { + mNextWallpaperComponent = null; + } + } + } + return changed; + } + } + public WallpaperManagerService(Context context) { - if (DEBUG) Log.v(TAG, "WallpaperService startup"); + if (DEBUG) Slog.v(TAG, "WallpaperService startup"); mContext = context; mIWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService(Context.WINDOW_SERVICE)); + mMonitor = new MyPackageMonitor(); + mMonitor.register(context, true); WALLPAPER_DIR.mkdirs(); loadSettingsLocked(); mWallpaperObserver.startWatching(); @@ -222,16 +310,16 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } public void systemReady() { - if (DEBUG) Log.v(TAG, "systemReady"); + if (DEBUG) Slog.v(TAG, "systemReady"); synchronized (mLock) { try { bindWallpaperComponentLocked(mNextWallpaperComponent); } catch (RuntimeException e) { - Log.w(TAG, "Failure starting previous wallpaper", e); + Slog.w(TAG, "Failure starting previous wallpaper", e); try { bindWallpaperComponentLocked(null); } catch (RuntimeException e2) { - Log.w(TAG, "Failure starting default wallpaper", e2); + Slog.w(TAG, "Failure starting default wallpaper", e2); clearWallpaperComponentLocked(); } } @@ -239,18 +327,28 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } public void clearWallpaper() { - if (DEBUG) Log.v(TAG, "clearWallpaper"); + if (DEBUG) Slog.v(TAG, "clearWallpaper"); synchronized (mLock) { - File f = WALLPAPER_FILE; - if (f.exists()) { - f.delete(); - } - final long ident = Binder.clearCallingIdentity(); - try { - bindWallpaperComponentLocked(null); - } finally { - Binder.restoreCallingIdentity(ident); - } + clearWallpaperLocked(); + } + } + + public void clearWallpaperLocked() { + File f = WALLPAPER_FILE; + if (f.exists()) { + f.delete(); + } + final long ident = Binder.clearCallingIdentity(); + try { + bindWallpaperComponentLocked(null); + } catch (IllegalArgumentException e) { + // This can happen if the default wallpaper component doesn't + // exist. This should be a system configuration problem, but + // let's not let it crash the system and just live with no + // wallpaper. + Slog.e(TAG, "Default wallpaper component not found!", e); + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -308,7 +406,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { return ParcelFileDescriptor.open(f, MODE_READ_ONLY); } catch (FileNotFoundException e) { /* Shouldn't happen as we check to see if the file exists */ - Log.w(TAG, "Error getting wallpaper", e); + Slog.w(TAG, "Error getting wallpaper", e); } return null; } @@ -324,7 +422,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } public ParcelFileDescriptor setWallpaper(String name) { - if (DEBUG) Log.v(TAG, "setWallpaper"); + if (DEBUG) Slog.v(TAG, "setWallpaper"); checkPermission(android.Manifest.permission.SET_WALLPAPER); synchronized (mLock) { @@ -351,13 +449,13 @@ class WallpaperManagerService extends IWallpaperManager.Stub { mName = name; return fd; } catch (FileNotFoundException e) { - Log.w(TAG, "Error setting wallpaper", e); + Slog.w(TAG, "Error setting wallpaper", e); } return null; } public void setWallpaperComponent(ComponentName name) { - if (DEBUG) Log.v(TAG, "setWallpaperComponent name=" + name); + if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name); checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); synchronized (mLock) { final long ident = Binder.clearCallingIdentity(); @@ -370,19 +468,19 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } void bindWallpaperComponentLocked(ComponentName componentName) { - if (DEBUG) Log.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName); + if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName); // Has the component changed? if (mWallpaperConnection != null) { if (mWallpaperComponent == null) { if (componentName == null) { - if (DEBUG) Log.v(TAG, "bindWallpaperComponentLocked: still using default"); + if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default"); // Still using default wallpaper. return; } } else if (mWallpaperComponent.equals(componentName)) { // Changing to same wallpaper. - if (DEBUG) Log.v(TAG, "same wallpaper"); + if (DEBUG) Slog.v(TAG, "same wallpaper"); return; } } @@ -394,14 +492,14 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (defaultComponent != null) { // See if there is a default wallpaper component specified componentName = ComponentName.unflattenFromString(defaultComponent); - if (DEBUG) Log.v(TAG, "Use default component walpaper:" + componentName); + if (DEBUG) Slog.v(TAG, "Use default component wallpaper:" + componentName); } if (componentName == null) { // Fall back to static image wallpaper componentName = mImageWallpaperComponent; //clearWallpaperComponentLocked(); //return; - if (DEBUG) Log.v(TAG, "Using image wallpaper"); + if (DEBUG) Slog.v(TAG, "Using image wallpaper"); } } ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName, @@ -440,7 +538,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } // Bind the service! - if (DEBUG) Log.v(TAG, "Binding to:" + componentName); + if (DEBUG) Slog.v(TAG, "Binding to:" + componentName); WallpaperConnection newConn = new WallpaperConnection(wi); intent.setComponent(componentName); intent.putExtra(Intent.EXTRA_CLIENT_LABEL, @@ -461,7 +559,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { mWallpaperConnection = newConn; mLastDiedTime = SystemClock.uptimeMillis(); try { - if (DEBUG) Log.v(TAG, "Adding window token: " + newConn.mToken); + if (DEBUG) Slog.v(TAG, "Adding window token: " + newConn.mToken); mIWindowManager.addWindowToken(newConn.mToken, WindowManager.LayoutParams.TYPE_WALLPAPER); } catch (RemoteException e) { @@ -483,7 +581,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } mContext.unbindService(mWallpaperConnection); try { - if (DEBUG) Log.v(TAG, "Removing window token: " + if (DEBUG) Slog.v(TAG, "Removing window token: " + mWallpaperConnection.mToken); mIWindowManager.removeWindowToken(mWallpaperConnection.mToken); } catch (RemoteException e) { @@ -498,8 +596,10 @@ class WallpaperManagerService extends IWallpaperManager.Stub { WindowManager.LayoutParams.TYPE_WALLPAPER, false, mWidth, mHeight); } catch (RemoteException e) { - Log.w(TAG, "Failed attaching wallpaper; clearing", e); - bindWallpaperComponentLocked(null); + Slog.w(TAG, "Failed attaching wallpaper; clearing", e); + if (!mWallpaperUpdating) { + bindWallpaperComponentLocked(null); + } } } @@ -566,7 +666,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } private void loadSettingsLocked() { - if (DEBUG) Log.v(TAG, "loadSettingsLocked"); + if (DEBUG) Slog.v(TAG, "loadSettingsLocked"); JournaledFile journal = makeJournaledFile(); FileInputStream stream = null; @@ -592,25 +692,25 @@ class WallpaperManagerService extends IWallpaperManager.Stub { : null; if (DEBUG) { - Log.v(TAG, "mWidth:" + mWidth); - Log.v(TAG, "mHeight:" + mHeight); - Log.v(TAG, "mName:" + mName); - Log.v(TAG, "mNextWallpaperComponent:" + mNextWallpaperComponent); + Slog.v(TAG, "mWidth:" + mWidth); + Slog.v(TAG, "mHeight:" + mHeight); + Slog.v(TAG, "mName:" + mName); + Slog.v(TAG, "mNextWallpaperComponent:" + mNextWallpaperComponent); } } } } while (type != XmlPullParser.END_DOCUMENT); success = true; } catch (NullPointerException e) { - Log.w(TAG, "failed parsing " + file + " " + e); + Slog.w(TAG, "failed parsing " + file + " " + e); } catch (NumberFormatException e) { - Log.w(TAG, "failed parsing " + file + " " + e); + Slog.w(TAG, "failed parsing " + file + " " + e); } catch (XmlPullParserException e) { - Log.w(TAG, "failed parsing " + file + " " + e); + Slog.w(TAG, "failed parsing " + file + " " + e); } catch (IOException e) { - Log.w(TAG, "failed parsing " + file + " " + e); + Slog.w(TAG, "failed parsing " + file + " " + e); } catch (IndexOutOfBoundsException e) { - Log.w(TAG, "failed parsing " + file + " " + e); + Slog.w(TAG, "failed parsing " + file + " " + e); } try { if (stream != null) { @@ -628,27 +728,33 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } void settingsRestored() { - if (DEBUG) Log.v(TAG, "settingsRestored"); + if (DEBUG) Slog.v(TAG, "settingsRestored"); boolean success = false; synchronized (mLock) { loadSettingsLocked(); if (mNextWallpaperComponent != null && !mNextWallpaperComponent.equals(mImageWallpaperComponent)) { - // We can't restore live wallpapers, so just go with the default - bindWallpaperComponentLocked(null); + try { + bindWallpaperComponentLocked(mNextWallpaperComponent); + } catch (IllegalArgumentException e) { + // No such live wallpaper or other failure; fall back to the default + // live wallpaper (since the profile being restored indicated that the + // user had selected a live rather than static one). + bindWallpaperComponentLocked(null); + } success = true; } else { // If there's a wallpaper name, we use that. If that can't be loaded, then we // use the default. if ("".equals(mName)) { - if (DEBUG) Log.v(TAG, "settingsRestored: name is empty"); + if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty"); success = true; } else { - if (DEBUG) Log.v(TAG, "settingsRestored: attempting to restore named resource"); + if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource"); success = restoreNamedResourceLocked(); } - if (DEBUG) Log.v(TAG, "settingsRestored: success=" + success); + if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success); if (success) { bindWallpaperComponentLocked(mImageWallpaperComponent); } @@ -656,7 +762,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } if (!success) { - Log.e(TAG, "Failed to restore wallpaper: '" + mName + "'"); + Slog.e(TAG, "Failed to restore wallpaper: '" + mName + "'"); mName = ""; WALLPAPER_FILE.delete(); } @@ -693,12 +799,15 @@ class WallpaperManagerService extends IWallpaperManager.Stub { Resources r = c.getResources(); resId = r.getIdentifier(resName, null, null); if (resId == 0) { - Log.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type + Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type + " ident=" + ident); return false; } res = r.openRawResource(resId); + if (WALLPAPER_FILE.exists()) { + WALLPAPER_FILE.delete(); + } fos = new FileOutputStream(WALLPAPER_FILE); byte[] buffer = new byte[32768]; @@ -708,14 +817,14 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } // mWallpaperObserver will notice the close and send the change broadcast - Log.v(TAG, "Restored wallpaper: " + resName); + Slog.v(TAG, "Restored wallpaper: " + resName); return true; } catch (NameNotFoundException e) { - Log.e(TAG, "Package name " + pkg + " not found"); + Slog.e(TAG, "Package name " + pkg + " not found"); } catch (Resources.NotFoundException e) { - Log.e(TAG, "Resource not found: " + resId); + Slog.e(TAG, "Resource not found: " + resId); } catch (IOException e) { - Log.e(TAG, "IOException while restoring wallpaper ", e); + Slog.e(TAG, "IOException while restoring wallpaper ", e); } finally { if (res != null) { try { diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index 68bf4fb..be1d1c4 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -29,13 +29,16 @@ import android.os.Debug; import android.os.Handler; import android.os.Message; import android.os.Process; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; import android.util.Config; import android.util.EventLog; import android.util.Log; +import android.util.Slog; +import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; @@ -52,16 +55,6 @@ public class Watchdog extends Thread { static final int GLOBAL_PSS = 2719; static final int TIME_TO_WAIT = DB ? 15*1000 : 60*1000; - static final int EVENT_LOG_TAG = 2802; - static final int EVENT_LOG_PROC_PSS_TAG = 2803; - static final int EVENT_LOG_SOFT_RESET_TAG = 2804; - static final int EVENT_LOG_HARD_RESET_TAG = 2805; - static final int EVENT_LOG_PSS_STATS_TAG = 2806; - static final int EVENT_LOG_PROC_STATS_TAG = 2807; - static final int EVENT_LOG_SCHEDULED_REBOOT_TAG = 2808; - static final int EVENT_LOG_MEMINFO_TAG = 2809; - static final int EVENT_LOG_VMSTAT_TAG = 2810; - static final int EVENT_LOG_REQUESTED_REBOOT_TAG = 2811; static final int MEMCHECK_DEFAULT_INTERVAL = DB ? 30 : 30*60; // 30 minutes static final int MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL = DB ? 60 : 2*60*60; // 2 hours @@ -107,16 +100,16 @@ public class Watchdog extends Thread { long mLastMemCheckRealtime = -(MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL*1000); boolean mHaveGlobalPss; final MemMonitor mSystemMemMonitor = new MemMonitor("system", - Settings.Gservices.MEMCHECK_SYSTEM_ENABLED, - Settings.Gservices.MEMCHECK_SYSTEM_SOFT_THRESHOLD, + Settings.Secure.MEMCHECK_SYSTEM_ENABLED, + Settings.Secure.MEMCHECK_SYSTEM_SOFT_THRESHOLD, MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD, - Settings.Gservices.MEMCHECK_SYSTEM_HARD_THRESHOLD, + Settings.Secure.MEMCHECK_SYSTEM_HARD_THRESHOLD, MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD); final MemMonitor mPhoneMemMonitor = new MemMonitor("com.android.phone", - Settings.Gservices.MEMCHECK_PHONE_ENABLED, - Settings.Gservices.MEMCHECK_PHONE_SOFT_THRESHOLD, + Settings.Secure.MEMCHECK_PHONE_ENABLED, + Settings.Secure.MEMCHECK_PHONE_SOFT_THRESHOLD, MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD, - Settings.Gservices.MEMCHECK_PHONE_HARD_THRESHOLD, + Settings.Secure.MEMCHECK_PHONE_HARD_THRESHOLD, MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD); final Calendar mCalendar = Calendar.getInstance(); @@ -171,11 +164,11 @@ public class Watchdog extends Thread { } void retrieveSettings(ContentResolver resolver) { - mSoftThreshold = Settings.Gservices.getInt( + mSoftThreshold = Settings.Secure.getInt( resolver, mSoftSetting, mSoftThreshold); - mHardThreshold = Settings.Gservices.getInt( + mHardThreshold = Settings.Secure.getInt( resolver, mHardSetting, mHardThreshold); - mEnabled = Settings.Gservices.getInt( + mEnabled = Settings.Secure.getInt( resolver, mEnabledSetting, 0) != 0; } @@ -188,7 +181,7 @@ public class Watchdog extends Thread { } else { mState = STATE_HARD; } - EventLog.writeEvent(EVENT_LOG_PROC_PSS_TAG, mProcessName, pid, mLastPss); + EventLog.writeEvent(EventLogTags.WATCHDOG_PROC_PSS, mProcessName, pid, mLastPss); if (mState == STATE_OK) { // Memory is good, don't recover. @@ -197,7 +190,7 @@ public class Watchdog extends Thread { if (mState == STATE_HARD) { // Memory is really bad, kill right now. - EventLog.writeEvent(EVENT_LOG_HARD_RESET_TAG, mProcessName, pid, + EventLog.writeEvent(EventLogTags.WATCHDOG_HARD_RESET, mProcessName, pid, mHardThreshold, mLastPss); return mEnabled; } @@ -212,7 +205,7 @@ public class Watchdog extends Thread { } else { skipReason = shouldWeBeBrutalLocked(curTime); } - EventLog.writeEvent(EVENT_LOG_SOFT_RESET_TAG, mProcessName, pid, + EventLog.writeEvent(EventLogTags.WATCHDOG_SOFT_RESET, mProcessName, pid, mSoftThreshold, mLastPss, skipReason != null ? skipReason : ""); if (skipReason != null) { mNeedScheduledCheck = true; @@ -239,7 +232,7 @@ public class Watchdog extends Thread { // During the last pass we collected pss information, so // now it is time to report it. mHaveGlobalPss = false; - if (localLOGV) Log.v(TAG, "Received global pss, logging."); + if (localLOGV) Slog.v(TAG, "Received global pss, logging."); logGlobalMemory(); } } break; @@ -249,7 +242,7 @@ public class Watchdog extends Thread { // During the last pass we collected pss information, so // now it is time to report it. mHavePss = false; - if (localLOGV) Log.v(TAG, "Have pss, checking memory."); + if (localLOGV) Slog.v(TAG, "Have pss, checking memory."); checkMemory(); } @@ -257,7 +250,7 @@ public class Watchdog extends Thread { // During the last pass we collected pss information, so // now it is time to report it. mHaveGlobalPss = false; - if (localLOGV) Log.v(TAG, "Have global pss, logging."); + if (localLOGV) Slog.v(TAG, "Have global pss, logging."); logGlobalMemory(); } @@ -265,8 +258,8 @@ public class Watchdog extends Thread { // See if we should force a reboot. int rebootInterval = mReqRebootInterval >= 0 - ? mReqRebootInterval : Settings.Gservices.getInt( - mResolver, Settings.Gservices.REBOOT_INTERVAL, + ? mReqRebootInterval : Settings.Secure.getInt( + mResolver, Settings.Secure.REBOOT_INTERVAL, REBOOT_DEFAULT_INTERVAL); if (mRebootInterval != rebootInterval) { mRebootInterval = rebootInterval; @@ -276,8 +269,8 @@ public class Watchdog extends Thread { } // See if we should check memory conditions. - long memCheckInterval = Settings.Gservices.getLong( - mResolver, Settings.Gservices.MEMCHECK_INTERVAL, + long memCheckInterval = Settings.Secure.getLong( + mResolver, Settings.Secure.MEMCHECK_INTERVAL, MEMCHECK_DEFAULT_INTERVAL) * 1000; if ((mLastMemCheckTime+memCheckInterval) < now) { // It is now time to collect pss information. This @@ -285,17 +278,17 @@ public class Watchdog extends Thread { // things simple, we will assume that everyone has // reported back by the next MONITOR message. mLastMemCheckTime = now; - if (localLOGV) Log.v(TAG, "Collecting memory usage."); + if (localLOGV) Slog.v(TAG, "Collecting memory usage."); collectMemory(); mHavePss = true; - long memCheckRealtimeInterval = Settings.Gservices.getLong( - mResolver, Settings.Gservices.MEMCHECK_LOG_REALTIME_INTERVAL, + long memCheckRealtimeInterval = Settings.Secure.getLong( + mResolver, Settings.Secure.MEMCHECK_LOG_REALTIME_INTERVAL, MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL) * 1000; long realtimeNow = SystemClock.elapsedRealtime(); if ((mLastMemCheckRealtime+memCheckRealtimeInterval) < realtimeNow) { mLastMemCheckRealtime = realtimeNow; - if (localLOGV) Log.v(TAG, "Collecting global memory usage."); + if (localLOGV) Slog.v(TAG, "Collecting global memory usage."); collectGlobalMemory(); mHaveGlobalPss = true; } @@ -325,7 +318,7 @@ public class Watchdog extends Thread { final class CheckupReceiver extends BroadcastReceiver { @Override public void onReceive(Context c, Intent intent) { - if (localLOGV) Log.v(TAG, "Alarm went off, checking memory."); + if (localLOGV) Slog.v(TAG, "Alarm went off, checking memory."); checkMemory(); } } @@ -333,7 +326,7 @@ public class Watchdog extends Thread { final class RebootReceiver extends BroadcastReceiver { @Override public void onReceive(Context c, Intent intent) { - if (localLOGV) Log.v(TAG, "Alarm went off, checking reboot."); + if (localLOGV) Slog.v(TAG, "Alarm went off, checking reboot."); checkReboot(true); } } @@ -348,7 +341,7 @@ public class Watchdog extends Thread { mReqMinScreenOff = intent.getIntExtra("minScreenOff", -1); mReqMinNextAlarm = intent.getIntExtra("minNextAlarm", -1); mReqRecheckInterval = intent.getIntExtra("recheckInterval", -1); - EventLog.writeEvent(EVENT_LOG_REQUESTED_REBOOT_TAG, + EventLog.writeEvent(EventLogTags.WATCHDOG_REQUESTED_REBOOT, mReqRebootNoWait ? 1 : 0, mReqRebootInterval, mReqRecheckInterval, mReqRebootStartTime, mReqRebootWindow, mReqMinScreenOff, mReqMinNextAlarm); @@ -478,8 +471,8 @@ public class Watchdog extends Thread { long curTime; long nextTime = 0; - long recheckInterval = Settings.Gservices.getLong( - mResolver, Settings.Gservices.MEMCHECK_RECHECK_INTERVAL, + long recheckInterval = Settings.Secure.getLong( + mResolver, Settings.Secure.MEMCHECK_RECHECK_INTERVAL, MEMCHECK_DEFAULT_RECHECK_INTERVAL) * 1000; mSystemMemMonitor.retrieveSettings(mResolver); @@ -504,7 +497,7 @@ public class Watchdog extends Thread { if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid, mPhonePss)) { // Just kill the phone process and let it restart. - Log.i(TAG, "Watchdog is killing the phone process"); + Slog.i(TAG, "Watchdog is killing the phone process"); Process.killProcess(mPhonePid); } } else { @@ -521,25 +514,25 @@ public class Watchdog extends Thread { } else if (nextTime >= mMemcheckExecEndTime){ // Need to check during next exec time... so that needs // to be computed. - if (localLOGV) Log.v(TAG, "Computing next time range"); + if (localLOGV) Slog.v(TAG, "Computing next time range"); computeMemcheckTimesLocked(nextTime); nextTime = mMemcheckExecStartTime; } if (localLOGV) { mCalendar.setTimeInMillis(nextTime); - Log.v(TAG, "Next Alarm Time: " + mCalendar); + Slog.v(TAG, "Next Alarm Time: " + mCalendar); } } } if (needScheduledCheck) { - if (localLOGV) Log.v(TAG, "Scheduling next memcheck alarm for " + if (localLOGV) Slog.v(TAG, "Scheduling next memcheck alarm for " + ((nextTime-curTime)/1000/60) + "m from now"); mAlarm.remove(mCheckupIntent); mAlarm.set(AlarmManager.RTC_WAKEUP, nextTime, mCheckupIntent); } else { - if (localLOGV) Log.v(TAG, "No need to schedule a memcheck alarm!"); + if (localLOGV) Slog.v(TAG, "No need to schedule a memcheck alarm!"); mAlarm.remove(mCheckupIntent); } } @@ -561,21 +554,21 @@ public class Watchdog extends Thread { void logGlobalMemory() { PssStats stats = mPssStats; mActivity.collectPss(stats); - EventLog.writeEvent(EVENT_LOG_PSS_STATS_TAG, + EventLog.writeEvent(EventLogTags.WATCHDOG_PSS_STATS, stats.mEmptyPss, stats.mEmptyCount, stats.mBackgroundPss, stats.mBackgroundCount, stats.mServicePss, stats.mServiceCount, stats.mVisiblePss, stats.mVisibleCount, stats.mForegroundPss, stats.mForegroundCount, stats.mNoPssCount); - EventLog.writeEvent(EVENT_LOG_PROC_STATS_TAG, + EventLog.writeEvent(EventLogTags.WATCHDOG_PROC_STATS, stats.mProcDeaths[0], stats.mProcDeaths[1], stats.mProcDeaths[2], stats.mProcDeaths[3], stats.mProcDeaths[4]); Process.readProcLines("/proc/meminfo", mMemInfoFields, mMemInfoSizes); for (int i=0; i<mMemInfoSizes.length; i++) { mMemInfoSizes[i] *= 1024; } - EventLog.writeEvent(EVENT_LOG_MEMINFO_TAG, + EventLog.writeEvent(EventLogTags.WATCHDOG_MEMINFO, (int)mMemInfoSizes[0], (int)mMemInfoSizes[1], (int)mMemInfoSizes[2], (int)mMemInfoSizes[3], (int)mMemInfoSizes[4], (int)mMemInfoSizes[5], (int)mMemInfoSizes[6], (int)mMemInfoSizes[7], @@ -589,35 +582,35 @@ public class Watchdog extends Thread { mVMStatSizes[i] -= mPrevVMStatSizes[i]; mPrevVMStatSizes[i] = v; } - EventLog.writeEvent(EVENT_LOG_VMSTAT_TAG, dur, + EventLog.writeEvent(EventLogTags.WATCHDOG_VMSTAT, dur, (int)mVMStatSizes[0], (int)mVMStatSizes[1], (int)mVMStatSizes[2], (int)mVMStatSizes[3], (int)mVMStatSizes[4]); } void checkReboot(boolean fromAlarm) { int rebootInterval = mReqRebootInterval >= 0 ? mReqRebootInterval - : Settings.Gservices.getInt( - mResolver, Settings.Gservices.REBOOT_INTERVAL, + : Settings.Secure.getInt( + mResolver, Settings.Secure.REBOOT_INTERVAL, REBOOT_DEFAULT_INTERVAL); mRebootInterval = rebootInterval; if (rebootInterval <= 0) { // No reboot interval requested. - if (localLOGV) Log.v(TAG, "No need to schedule a reboot alarm!"); + if (localLOGV) Slog.v(TAG, "No need to schedule a reboot alarm!"); mAlarm.remove(mRebootIntent); return; } long rebootStartTime = mReqRebootStartTime >= 0 ? mReqRebootStartTime - : Settings.Gservices.getLong( - mResolver, Settings.Gservices.REBOOT_START_TIME, + : Settings.Secure.getLong( + mResolver, Settings.Secure.REBOOT_START_TIME, REBOOT_DEFAULT_START_TIME); long rebootWindowMillis = (mReqRebootWindow >= 0 ? mReqRebootWindow - : Settings.Gservices.getLong( - mResolver, Settings.Gservices.REBOOT_WINDOW, + : Settings.Secure.getLong( + mResolver, Settings.Secure.REBOOT_WINDOW, REBOOT_DEFAULT_WINDOW)) * 1000; long recheckInterval = (mReqRecheckInterval >= 0 ? mReqRecheckInterval - : Settings.Gservices.getLong( - mResolver, Settings.Gservices.MEMCHECK_RECHECK_INTERVAL, + : Settings.Secure.getLong( + mResolver, Settings.Secure.MEMCHECK_RECHECK_INTERVAL, MEMCHECK_DEFAULT_RECHECK_INTERVAL)) * 1000; retrieveBrutalityAmount(); @@ -635,7 +628,7 @@ public class Watchdog extends Thread { (now-mBootTime) >= (rebootIntervalMillis-rebootWindowMillis)) { if (fromAlarm && rebootWindowMillis <= 0) { // No reboot window -- just immediately reboot. - EventLog.writeEvent(EVENT_LOG_SCHEDULED_REBOOT_TAG, now, + EventLog.writeEvent(EventLogTags.WATCHDOG_SCHEDULED_REBOOT, now, (int)rebootIntervalMillis, (int)rebootStartTime*1000, (int)rebootWindowMillis, ""); rebootSystem("Checkin scheduled forced"); @@ -649,7 +642,7 @@ public class Watchdog extends Thread { now, rebootStartTime); } else if (now < (realStartTime+rebootWindowMillis)) { String doit = shouldWeBeBrutalLocked(now); - EventLog.writeEvent(EVENT_LOG_SCHEDULED_REBOOT_TAG, now, + EventLog.writeEvent(EventLogTags.WATCHDOG_SCHEDULED_REBOOT, now, (int)rebootInterval, (int)rebootStartTime*1000, (int)rebootWindowMillis, doit != null ? doit : ""); if (doit == null) { @@ -673,7 +666,7 @@ public class Watchdog extends Thread { } } - if (localLOGV) Log.v(TAG, "Scheduling next reboot alarm for " + if (localLOGV) Slog.v(TAG, "Scheduling next reboot alarm for " + ((realStartTime-now)/1000/60) + "m from now"); mAlarm.remove(mRebootIntent); mAlarm.set(AlarmManager.RTC_WAKEUP, realStartTime, mRebootIntent); @@ -683,12 +676,9 @@ public class Watchdog extends Thread { * Perform a full reboot of the system. */ void rebootSystem(String reason) { - Log.i(TAG, "Rebooting system because: " + reason); - try { - android.os.Power.reboot(reason); - } catch (IOException e) { - Log.e(TAG, "Reboot failed!", e); - } + Slog.i(TAG, "Rebooting system because: " + reason); + PowerManagerService pms = (PowerManagerService) ServiceManager.getService("power"); + pms.reboot(reason); } /** @@ -698,12 +688,12 @@ public class Watchdog extends Thread { */ void retrieveBrutalityAmount() { mMinScreenOff = (mReqMinScreenOff >= 0 ? mReqMinScreenOff - : Settings.Gservices.getInt( - mResolver, Settings.Gservices.MEMCHECK_MIN_SCREEN_OFF, + : Settings.Secure.getInt( + mResolver, Settings.Secure.MEMCHECK_MIN_SCREEN_OFF, MEMCHECK_DEFAULT_MIN_SCREEN_OFF)) * 1000; mMinAlarm = (mReqMinNextAlarm >= 0 ? mReqMinNextAlarm - : Settings.Gservices.getInt( - mResolver, Settings.Gservices.MEMCHECK_MIN_ALARM, + : Settings.Secure.getInt( + mResolver, Settings.Secure.MEMCHECK_MIN_ALARM, MEMCHECK_DEFAULT_MIN_ALARM)) * 1000; } @@ -747,11 +737,11 @@ public class Watchdog extends Thread { mMemcheckLastTime = curTime; - long memcheckExecStartTime = Settings.Gservices.getLong( - mResolver, Settings.Gservices.MEMCHECK_EXEC_START_TIME, + long memcheckExecStartTime = Settings.Secure.getLong( + mResolver, Settings.Secure.MEMCHECK_EXEC_START_TIME, MEMCHECK_DEFAULT_EXEC_START_TIME); - long memcheckExecEndTime = Settings.Gservices.getLong( - mResolver, Settings.Gservices.MEMCHECK_EXEC_END_TIME, + long memcheckExecEndTime = Settings.Secure.getLong( + mResolver, Settings.Secure.MEMCHECK_EXEC_END_TIME, MEMCHECK_DEFAULT_EXEC_END_TIME); mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime, @@ -767,11 +757,11 @@ public class Watchdog extends Thread { if (localLOGV) { mCalendar.setTimeInMillis(curTime); - Log.v(TAG, "Current Time: " + mCalendar); + Slog.v(TAG, "Current Time: " + mCalendar); mCalendar.setTimeInMillis(mMemcheckExecStartTime); - Log.v(TAG, "Start Check Time: " + mCalendar); + Slog.v(TAG, "Start Check Time: " + mCalendar); mCalendar.setTimeInMillis(mMemcheckExecEndTime); - Log.v(TAG, "End Check Time: " + mCalendar); + Slog.v(TAG, "End Check Time: " + mCalendar); } } @@ -814,19 +804,14 @@ public class Watchdog extends Thread { // to timeout on is asleep as well and won't have a chance to run. Causing a false // positive on when to kill things. long start = SystemClock.uptimeMillis(); - do { + while (timeout > 0 && !mForceKillSystem) { try { - wait(timeout); + wait(timeout); // notifyAll() is called when mForceKillSystem is set } catch (InterruptedException e) { - if (SystemProperties.getBoolean("ro.secure", false)) { - // If this is a secure build, just log the error. - Log.e("WatchDog", "Woof! Woof! Interrupter!"); - } else { - throw new AssertionError("Someone interrupted the watchdog"); - } + Log.wtf(TAG, e); } timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start); - } while (timeout > 0 && !mForceKillSystem); + } if (mCompleted && !mForceKillSystem) { // The monitors have returned. @@ -835,22 +820,30 @@ public class Watchdog extends Thread { } // If we got here, that means that the system is most likely hung. - // First send a SIGQUIT so that we can see where it was hung. Then - // kill this process so that the system will restart. + // First collect stack traces from all threads of the system process. + // Then kill this process so that the system will restart. + String name = (mCurrentMonitor != null) ? mCurrentMonitor.getClass().getName() : "null"; - EventLog.writeEvent(EVENT_LOG_TAG, name); - Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT); + EventLog.writeEvent(EventLogTags.WATCHDOG, name); - // Wait a bit longer before killing so we can make sure that the stacks are captured. - try { - Thread.sleep(10*1000); - } catch (InterruptedException e) { - } + ArrayList pids = new ArrayList(); + pids.add(Process.myPid()); + if (mPhonePid > 0) pids.add(mPhonePid); + File stack = ActivityManagerService.dumpStackTraces(pids); + + // Give some extra time to make sure the stack traces get written. + // The system's been hanging for a minute, another second or two won't hurt much. + SystemClock.sleep(2000); + + mActivity.addErrorToDropBox("watchdog", null, null, null, name, null, stack, null); // Only kill the process if the debugger is not attached. if (!Debug.isDebuggerConnected()) { - Log.i(TAG, "Watchdog is killing the system process"); + Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name); Process.killProcess(Process.myPid()); + System.exit(10); + } else { + Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process"); } } } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 3a08e4d..c0a4491 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -22,6 +22,12 @@ import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; + import android.app.AlarmManager; import android.app.PendingIntent; import android.bluetooth.BluetoothA2dp; @@ -40,6 +46,9 @@ import android.net.wifi.WifiStateTracker; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.SupplicantState; +import android.net.wifi.WifiConfiguration.KeyMgmt; +import android.net.ConnectivityManager; +import android.net.InterfaceConfiguration; import android.net.NetworkStateTracker; import android.net.DhcpInfo; import android.net.NetworkUtils; @@ -47,6 +56,7 @@ import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.PowerManager; @@ -54,7 +64,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; -import android.util.Log; +import android.util.Slog; import android.text.TextUtils; import java.util.ArrayList; @@ -67,10 +77,12 @@ import java.util.Set; import java.util.regex.Pattern; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.net.UnknownHostException; import com.android.internal.app.IBatteryStats; -import android.backup.IBackupManager; +import android.app.backup.IBackupManager; import com.android.server.am.BatteryStatsService; +import com.android.internal.R; /** * WifiService handles remote WiFi operation requests by implementing @@ -84,9 +96,11 @@ public class WifiService extends IWifiManager.Stub { private static final boolean DBG = false; private static final Pattern scanResultPattern = Pattern.compile("\t+"); private final WifiStateTracker mWifiStateTracker; + /* TODO: fetch a configurable interface */ + private static final String SOFTAP_IFACE = "wl0.1"; private Context mContext; - private int mWifiState; + private int mWifiApState; private AlarmManager mAlarmManager; private PendingIntent mIdleIntent; @@ -95,6 +109,8 @@ public class WifiService extends IWifiManager.Stub { private boolean mDeviceIdle; private int mPluggedType; + private enum DriverAction {DRIVER_UNLOAD, NO_DRIVER_UNLOAD}; + // true if the user enabled Wifi while in airplane mode private boolean mAirplaneModeOverwridden; @@ -112,9 +128,14 @@ public class WifiService extends IWifiManager.Stub { private final IBatteryStats mBatteryStats; + private INetworkManagementService nwService; + ConnectivityManager mCm; + private WifiWatchdogService mWifiWatchdogService = null; + private String[] mWifiRegexs; + /** - * 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 + * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a + * Settings.Secure 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. @@ -139,11 +160,16 @@ public class WifiService extends IWifiManager.Stub { // Wake lock used by other operations private static PowerManager.WakeLock sWakeLock; - private static final int MESSAGE_ENABLE_WIFI = 0; - private static final int MESSAGE_DISABLE_WIFI = 1; - private static final int MESSAGE_STOP_WIFI = 2; - private static final int MESSAGE_START_WIFI = 3; - private static final int MESSAGE_RELEASE_WAKELOCK = 4; + private static final int MESSAGE_ENABLE_WIFI = 0; + private static final int MESSAGE_DISABLE_WIFI = 1; + private static final int MESSAGE_STOP_WIFI = 2; + private static final int MESSAGE_START_WIFI = 3; + private static final int MESSAGE_RELEASE_WAKELOCK = 4; + private static final int MESSAGE_UPDATE_STATE = 5; + private static final int MESSAGE_START_ACCESS_POINT = 6; + private static final int MESSAGE_STOP_ACCESS_POINT = 7; + private static final int MESSAGE_SET_CHANNELS = 8; + private final WifiHandler mWifiHandler; @@ -164,6 +190,12 @@ public class WifiService extends IWifiManager.Stub { */ private int mLastEnableUid = Process.myUid(); + /* + * Last UID that asked to enable WIFI AP. + */ + private int mLastApEnableUid = Process.myUid(); + + /** * Number of allowed radio frequency channels in various regulatory domains. * This list is sufficient for 802.11b/g networks (2.4GHz range). @@ -179,6 +211,9 @@ public class WifiService extends IWifiManager.Stub { mWifiStateTracker.enableRssiPolling(true); mBatteryStats = BatteryStatsService.getService(); + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + nwService = INetworkManagementService.Stub.asInterface(b); + mScanResultCache = new LinkedHashMap<String, ScanResult>( SCAN_RESULT_CACHE_SIZE, 0.75f, true) { /* @@ -194,8 +229,8 @@ public class WifiService extends IWifiManager.Stub { wifiThread.start(); mWifiHandler = new WifiHandler(wifiThread.getLooper()); - mWifiState = WIFI_STATE_DISABLED; - boolean wifiEnabled = getPersistedWifiEnabled(); + mWifiStateTracker.setWifiState(WIFI_STATE_DISABLED); + mWifiApState = WIFI_AP_STATE_DISABLED; mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); @@ -204,21 +239,6 @@ public class WifiService extends IWifiManager.Stub { PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); - mWifiStateTracker.setReleaseWakeLockCallback( - new Runnable() { - public void run() { - mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK); - synchronized (sDriverStopWakeLock) { - if (sDriverStopWakeLock.isHeld()) { - sDriverStopWakeLock.release(); - } - } - } - } - ); - - Log.i(TAG, "WifiService starting up with Wi-Fi " + - (wifiEnabled ? "enabled" : "disabled")); mContext.registerReceiver( new BroadcastReceiver() { @@ -226,12 +246,102 @@ public class WifiService extends IWifiManager.Stub { public void onReceive(Context context, Intent intent) { // clear our flag indicating the user has overwridden airplane mode mAirplaneModeOverwridden = false; + // on airplane disable, restore Wifi if the saved state indicates so + if (!isAirplaneModeOn() && testAndClearWifiSavedState()) { + persistWifiEnabled(true); + } updateWifiState(); } }, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); - setWifiEnabledBlocking(wifiEnabled, false, Process.myUid()); + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + + ArrayList<String> available = intent.getStringArrayListExtra( + ConnectivityManager.EXTRA_AVAILABLE_TETHER); + ArrayList<String> active = intent.getStringArrayListExtra( + ConnectivityManager.EXTRA_ACTIVE_TETHER); + updateTetherState(available, active); + + } + },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); + } + + /** + * Check if Wi-Fi needs to be enabled and start + * if needed + * + * This function is used only at boot time + */ + public void startWifi() { + /* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */ + boolean wifiEnabled = !isAirplaneModeOn() + && (getPersistedWifiEnabled() || testAndClearWifiSavedState()); + Slog.i(TAG, "WifiService starting up with Wi-Fi " + + (wifiEnabled ? "enabled" : "disabled")); + setWifiEnabled(wifiEnabled); + } + + private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) { + + boolean wifiTethered = false; + boolean wifiAvailable = false; + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + + mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + mWifiRegexs = mCm.getTetherableWifiRegexs(); + + for (String intf : available) { + for (String regex : mWifiRegexs) { + if (intf.matches(regex)) { + + InterfaceConfiguration ifcg = null; + try { + ifcg = service.getInterfaceConfig(intf); + if (ifcg != null) { + /* IP/netmask: 192.168.43.1/255.255.255.0 */ + ifcg.ipAddr = (192 << 24) + (168 << 16) + (43 << 8) + 1; + ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0; + ifcg.interfaceFlags = "up"; + + service.setInterfaceConfig(intf, ifcg); + } + } catch (Exception e) { + Slog.e(TAG, "Error configuring interface " + intf + ", :" + e); + try { + nwService.stopAccessPoint(); + } catch (Exception ee) { + Slog.e(TAG, "Could not stop AP, :" + ee); + } + setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD); + return; + } + + if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + Slog.e(TAG, "Error tethering "+intf); + } + break; + } + } + } + } + + private boolean testAndClearWifiSavedState() { + final ContentResolver cr = mContext.getContentResolver(); + int wifiSavedState = 0; + try { + wifiSavedState = Settings.Secure.getInt(cr, Settings.Secure.WIFI_SAVED_STATE); + if(wifiSavedState == 1) + Settings.Secure.putInt(cr, Settings.Secure.WIFI_SAVED_STATE, 0); + } catch (Settings.SettingNotFoundException e) { + ; + } + return (wifiSavedState == 1); } private boolean getPersistedWifiEnabled() { @@ -259,9 +369,8 @@ public class WifiService extends IWifiManager.Stub { */ public boolean pingSupplicant() { enforceChangePermission(); - synchronized (mWifiStateTracker) { - return WifiNative.pingCommand(); - } + + return mWifiStateTracker.ping(); } /** @@ -270,20 +379,19 @@ public class WifiService extends IWifiManager.Stub { */ public boolean startScan(boolean forceActive) { enforceChangePermission(); - synchronized (mWifiStateTracker) { - switch (mWifiStateTracker.getSupplicantState()) { - case DISCONNECTED: - case INACTIVE: - case SCANNING: - case DORMANT: - break; - default: - WifiNative.setScanResultHandlingCommand( - WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY); - break; - } - return WifiNative.scanCommand(forceActive); + + switch (mWifiStateTracker.getSupplicantState()) { + case DISCONNECTED: + case INACTIVE: + case SCANNING: + case DORMANT: + break; + default: + mWifiStateTracker.setScanResultHandling( + WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY); + break; } + return mWifiStateTracker.scan(forceActive); } /** @@ -321,8 +429,9 @@ public class WifiService extends IWifiManager.Stub { */ private boolean setWifiEnabledBlocking(boolean enable, boolean persist, int uid) { final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED; + final int wifiState = mWifiStateTracker.getWifiState(); - if (mWifiState == eventualWifiState) { + if (wifiState == eventualWifiState) { return true; } if (enable && isAirplaneModeOn() && !mAirplaneModeOverwridden) { @@ -336,26 +445,38 @@ public class WifiService extends IWifiManager.Stub { * Avoid doing a disable when the current Wifi state is UNKNOWN * TODO: Handle driver load fail and supplicant lost as seperate states */ - if (mWifiState == WIFI_STATE_UNKNOWN && !enable) { + if ((wifiState == WIFI_STATE_UNKNOWN) && !enable) { + return false; + } + + /** + * Fail Wifi if AP is enabled + * TODO: Deprecate WIFI_STATE_UNKNOWN and rename it + * WIFI_STATE_FAILED + */ + if ((mWifiApState == WIFI_AP_STATE_ENABLED) && enable) { + setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); return false; } setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid); if (enable) { - if (!WifiNative.loadDriver()) { - Log.e(TAG, "Failed to load Wi-Fi driver."); + if (!mWifiStateTracker.loadDriver()) { + Slog.e(TAG, "Failed to load Wi-Fi driver."); setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); return false; } - if (!WifiNative.startSupplicant()) { - WifiNative.unloadDriver(); - Log.e(TAG, "Failed to start supplicant daemon."); + if (!mWifiStateTracker.startSupplicant()) { + mWifiStateTracker.unloadDriver(); + Slog.e(TAG, "Failed to start supplicant daemon."); setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); return false; } + registerForBroadcasts(); mWifiStateTracker.startEventLoop(); + } else { mContext.unregisterReceiver(mReceiver); @@ -363,22 +484,27 @@ public class WifiService extends IWifiManager.Stub { mWifiStateTracker.setNotificationVisible(false, 0, false, 0); boolean failedToStopSupplicantOrUnloadDriver = false; - if (!WifiNative.stopSupplicant()) { - Log.e(TAG, "Failed to stop supplicant daemon."); + + if (!mWifiStateTracker.stopSupplicant()) { + Slog.e(TAG, "Failed to stop supplicant daemon."); setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); failedToStopSupplicantOrUnloadDriver = true; } - // We must reset the interface before we unload the driver - mWifiStateTracker.resetInterface(false); + /** + * Reset connections and disable interface + * before we unload the driver + */ + mWifiStateTracker.resetConnections(true); - if (!WifiNative.unloadDriver()) { - Log.e(TAG, "Failed to unload Wi-Fi driver."); + if (!mWifiStateTracker.unloadDriver()) { + Slog.e(TAG, "Failed to unload Wi-Fi driver."); if (!failedToStopSupplicantOrUnloadDriver) { setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); failedToStopSupplicantOrUnloadDriver = true; } } + if (failedToStopSupplicantOrUnloadDriver) { return false; } @@ -390,12 +516,11 @@ public class WifiService extends IWifiManager.Stub { persistWifiEnabled(enable); } setWifiEnabledState(eventualWifiState, uid); - return true; } private void setWifiEnabledState(int wifiState, int uid) { - final int previousWifiState = mWifiState; + final int previousWifiState = mWifiStateTracker.getWifiState(); long ident = Binder.clearCallingIdentity(); try { @@ -410,7 +535,7 @@ public class WifiService extends IWifiManager.Stub { } // Update state - mWifiState = wifiState; + mWifiStateTracker.setWifiState(wifiState); // Broadcast final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); @@ -447,7 +572,7 @@ public class WifiService extends IWifiManager.Stub { */ public int getWifiEnabledState() { enforceAccessPermission(); - return mWifiState; + return mWifiStateTracker.getWifiState(); } /** @@ -456,9 +581,8 @@ public class WifiService extends IWifiManager.Stub { */ public boolean disconnect() { enforceChangePermission(); - synchronized (mWifiStateTracker) { - return WifiNative.disconnectCommand(); - } + + return mWifiStateTracker.disconnect(); } /** @@ -467,9 +591,8 @@ public class WifiService extends IWifiManager.Stub { */ public boolean reconnect() { enforceChangePermission(); - synchronized (mWifiStateTracker) { - return WifiNative.reconnectCommand(); - } + + return mWifiStateTracker.reconnectCommand(); } /** @@ -478,9 +601,204 @@ public class WifiService extends IWifiManager.Stub { */ public boolean reassociate() { enforceChangePermission(); - synchronized (mWifiStateTracker) { - return WifiNative.reassociateCommand(); + + return mWifiStateTracker.reassociate(); + } + + /** + * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)} + * @param wifiConfig SSID, security and channel details as + * part of WifiConfiguration + * @param enabled, true to enable and false to disable + * @return {@code true} if the start operation was + * started or is already in the queue. + */ + public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { + enforceChangePermission(); + if (mWifiHandler == null) return false; + + synchronized (mWifiHandler) { + + long ident = Binder.clearCallingIdentity(); + sWakeLock.acquire(); + Binder.restoreCallingIdentity(ident); + + mLastApEnableUid = Binder.getCallingUid(); + sendAccessPointMessage(enabled, wifiConfig, Binder.getCallingUid()); + } + + return true; + } + + public WifiConfiguration getWifiApConfiguration() { + final ContentResolver cr = mContext.getContentResolver(); + WifiConfiguration wifiConfig = new WifiConfiguration(); + int authType; + try { + wifiConfig.SSID = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_SSID); + if (wifiConfig.SSID == null) + return null; + authType = Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_SECURITY); + wifiConfig.allowedKeyManagement.set(authType); + wifiConfig.preSharedKey = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_PASSWD); + return wifiConfig; + } catch (Settings.SettingNotFoundException e) { + Slog.e(TAG,"AP settings not found, returning"); + return null; + } + } + + private void persistApConfiguration(WifiConfiguration wifiConfig) { + final ContentResolver cr = mContext.getContentResolver(); + boolean isWpa; + if (wifiConfig == null) + return; + Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_SSID, wifiConfig.SSID); + isWpa = wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK); + Settings.Secure.putInt(cr, + Settings.Secure.WIFI_AP_SECURITY, + isWpa ? KeyMgmt.WPA_PSK : KeyMgmt.NONE); + if (isWpa) + Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_PASSWD, wifiConfig.preSharedKey); + } + + /** + * Enables/disables Wi-Fi AP synchronously. The driver is loaded + * and soft access point configured as a single operation. + * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off. + * @param uid The UID of the process making the request. + * @param wifiConfig The WifiConfiguration for AP + * @return {@code true} if the operation succeeds (or if the existing state + * is the same as the requested state) + */ + private boolean setWifiApEnabledBlocking(boolean enable, + int uid, WifiConfiguration wifiConfig) { + final int eventualWifiApState = enable ? WIFI_AP_STATE_ENABLED : WIFI_AP_STATE_DISABLED; + + if (mWifiApState == eventualWifiApState) { + /* Configuration changed on a running access point */ + if(enable && (wifiConfig != null)) { + try { + persistApConfiguration(wifiConfig); + nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(), + SOFTAP_IFACE); + return true; + } catch(Exception e) { + Slog.e(TAG, "Exception in nwService during AP restart"); + try { + nwService.stopAccessPoint(); + } catch (Exception ee) { + Slog.e(TAG, "Could not stop AP, :" + ee); + } + setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD); + return false; + } + } else { + return true; + } + } + + /** + * Fail AP if Wifi is enabled + */ + if ((mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLED) && enable) { + setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD); + return false; + } + + setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING : + WIFI_AP_STATE_DISABLING, uid, DriverAction.NO_DRIVER_UNLOAD); + + if (enable) { + + /* Use default config if there is no existing config */ + if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) { + wifiConfig = new WifiConfiguration(); + wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default); + wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE); + } + persistApConfiguration(wifiConfig); + + if (!mWifiStateTracker.loadDriver()) { + Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode"); + setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD); + return false; + } + + try { + nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(), + SOFTAP_IFACE); + } catch(Exception e) { + Slog.e(TAG, "Exception in startAccessPoint()"); + setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD); + return false; + } + + } else { + + try { + nwService.stopAccessPoint(); + } catch(Exception e) { + Slog.e(TAG, "Exception in stopAccessPoint()"); + setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD); + return false; + } + + if (!mWifiStateTracker.unloadDriver()) { + Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode"); + setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD); + return false; + } } + + setWifiApEnabledState(eventualWifiApState, uid, DriverAction.NO_DRIVER_UNLOAD); + return true; + } + + /** + * see {@link WifiManager#getWifiApState()} + * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, + * {@link WifiManager#WIFI_AP_STATE_DISABLING}, + * {@link WifiManager#WIFI_AP_STATE_ENABLED}, + * {@link WifiManager#WIFI_AP_STATE_ENABLING}, + * {@link WifiManager#WIFI_AP_STATE_FAILED} + */ + public int getWifiApEnabledState() { + enforceAccessPermission(); + return mWifiApState; + } + + private void setWifiApEnabledState(int wifiAPState, int uid, DriverAction flag) { + final int previousWifiApState = mWifiApState; + + /** + * Unload the driver if going to a failed state + */ + if ((mWifiApState == WIFI_AP_STATE_FAILED) && (flag == DriverAction.DRIVER_UNLOAD)) { + mWifiStateTracker.unloadDriver(); + } + + long ident = Binder.clearCallingIdentity(); + try { + if (wifiAPState == WIFI_AP_STATE_ENABLED) { + mBatteryStats.noteWifiOn(uid); + } else if (wifiAPState == WIFI_AP_STATE_DISABLED) { + mBatteryStats.noteWifiOff(uid); + } + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + + // Update state + mWifiApState = wifiAPState; + + // Broadcast + final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiAPState); + intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState); + mContext.sendStickyBroadcast(intent); } /** @@ -490,15 +808,15 @@ public class WifiService extends IWifiManager.Stub { public List<WifiConfiguration> getConfiguredNetworks() { enforceAccessPermission(); String listStr; + /* * We don't cache the list, because we want to allow * for the possibility that the configuration file * has been modified through some external means, * such as the wpa_cli command line program. */ - synchronized (mWifiStateTracker) { - listStr = WifiNative.listNetworksCommand(); - } + listStr = mWifiStateTracker.listNetworks(); + List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); if (listStr == null) @@ -522,11 +840,10 @@ public class WifiService extends IWifiManager.Stub { config.status = WifiConfiguration.Status.DISABLED; else config.status = WifiConfiguration.Status.ENABLED; - } else + } else { config.status = WifiConfiguration.Status.ENABLED; - synchronized (mWifiStateTracker) { - readNetworkVariables(config); } + readNetworkVariables(config); networks.add(config); } @@ -540,7 +857,7 @@ public class WifiService extends IWifiManager.Stub { * The caller must hold the synchronization monitor. * @param config the {@link WifiConfiguration} object to be filled in. */ - private static void readNetworkVariables(WifiConfiguration config) { + private void readNetworkVariables(WifiConfiguration config) { int netId = config.networkId; if (netId < 0) @@ -553,21 +870,21 @@ public class WifiService extends IWifiManager.Stub { */ String value; - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.ssidVarName); if (!TextUtils.isEmpty(value)) { - config.SSID = value; + config.SSID = removeDoubleQuotes(value); } else { config.SSID = null; } - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.bssidVarName); if (!TextUtils.isEmpty(value)) { config.BSSID = value; } else { config.BSSID = null; } - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.priorityVarName); config.priority = -1; if (!TextUtils.isEmpty(value)) { try { @@ -576,7 +893,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName); config.hiddenSSID = false; if (!TextUtils.isEmpty(value)) { try { @@ -585,7 +902,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName); config.wepTxKeyIndex = -1; if (!TextUtils.isEmpty(value)) { try { @@ -599,7 +916,7 @@ public class WifiService extends IWifiManager.Stub { * just a "*" if the key is set, or the null string otherwise. */ for (int i = 0; i < 4; i++) { - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepKeyVarNames[i]); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepKeyVarNames[i]); if (!TextUtils.isEmpty(value)) { config.wepKeys[i] = value; } else { @@ -611,14 +928,14 @@ public class WifiService extends IWifiManager.Stub { * Get the private shared key. Note that the actual keys are not passed back, * just a "*" if the key is set, or the null string otherwise. */ - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.pskVarName); if (!TextUtils.isEmpty(value)) { config.preSharedKey = value; } else { config.preSharedKey = null; } - value = WifiNative.getNetworkVariableCommand(config.networkId, + value = mWifiStateTracker.getNetworkVariable(config.networkId, WifiConfiguration.Protocol.varName); if (!TextUtils.isEmpty(value)) { String vals[] = value.split(" "); @@ -631,7 +948,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(config.networkId, + value = mWifiStateTracker.getNetworkVariable(config.networkId, WifiConfiguration.KeyMgmt.varName); if (!TextUtils.isEmpty(value)) { String vals[] = value.split(" "); @@ -644,7 +961,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(config.networkId, + value = mWifiStateTracker.getNetworkVariable(config.networkId, WifiConfiguration.AuthAlgorithm.varName); if (!TextUtils.isEmpty(value)) { String vals[] = value.split(" "); @@ -657,7 +974,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(config.networkId, + value = mWifiStateTracker.getNetworkVariable(config.networkId, WifiConfiguration.PairwiseCipher.varName); if (!TextUtils.isEmpty(value)) { String vals[] = value.split(" "); @@ -670,7 +987,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(config.networkId, + value = mWifiStateTracker.getNetworkVariable(config.networkId, WifiConfiguration.GroupCipher.varName); if (!TextUtils.isEmpty(value)) { String vals[] = value.split(" "); @@ -685,21 +1002,32 @@ public class WifiService extends IWifiManager.Stub { for (WifiConfiguration.EnterpriseField field : config.enterpriseFields) { - value = WifiNative.getNetworkVariableCommand(netId, + value = mWifiStateTracker.getNetworkVariable(netId, field.varName()); if (!TextUtils.isEmpty(value)) { + if (field != config.eap) value = removeDoubleQuotes(value); field.setValue(value); } } } + private static String removeDoubleQuotes(String string) { + if (string.length() <= 2) return ""; + return string.substring(1, string.length() - 1); + } + + private static String convertToQuotedString(String string) { + return "\"" + string + "\""; + } + /** * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)} * @return the supplicant-assigned identifier for the new or updated * network if the operation succeeds, or {@code -1} if it fails */ - public synchronized int addOrUpdateNetwork(WifiConfiguration config) { + public int addOrUpdateNetwork(WifiConfiguration config) { enforceChangePermission(); + /* * If the supplied networkId is -1, we create a new empty * network configuration. Otherwise, the networkId should @@ -707,57 +1035,47 @@ public class WifiService extends IWifiManager.Stub { */ int netId = config.networkId; boolean newNetwork = netId == -1; - boolean doReconfig; - int currentPriority; + boolean doReconfig = false; // networkId of -1 means we want to create a new network - if (newNetwork) { - netId = WifiNative.addNetworkCommand(); - if (netId < 0) { - if (DBG) { - Log.d(TAG, "Failed to add a network!"); - } - return -1; - } - doReconfig = true; - } else { - String priorityVal = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); - currentPriority = -1; - if (!TextUtils.isEmpty(priorityVal)) { - try { - currentPriority = Integer.parseInt(priorityVal); - } catch (NumberFormatException ignore) { + synchronized (mWifiStateTracker) { + if (newNetwork) { + netId = mWifiStateTracker.addNetwork(); + if (netId < 0) { + if (DBG) { + Slog.d(TAG, "Failed to add a network!"); + } + return -1; } + doReconfig = true; } - doReconfig = currentPriority != config.priority; + mNeedReconfig = mNeedReconfig || doReconfig; } - mNeedReconfig = mNeedReconfig || doReconfig; setVariables: { /* * Note that if a networkId for a non-existent network - * was supplied, then the first setNetworkVariableCommand() + * was supplied, then the first setNetworkVariable() * will fail, so we don't bother to make a separate check * for the validity of the ID up front. */ - if (config.SSID != null && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.ssidVarName, - config.SSID)) { + !mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.ssidVarName, + convertToQuotedString(config.SSID))) { if (DBG) { - Log.d(TAG, "failed to set SSID: "+config.SSID); + Slog.d(TAG, "failed to set SSID: "+config.SSID); } break setVariables; } if (config.BSSID != null && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.bssidVarName, - config.BSSID)) { + !mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.bssidVarName, + config.BSSID)) { if (DBG) { - Log.d(TAG, "failed to set BSSID: "+config.BSSID); + Slog.d(TAG, "failed to set BSSID: "+config.BSSID); } break setVariables; } @@ -765,13 +1083,13 @@ public class WifiService extends IWifiManager.Stub { String allowedKeyManagementString = makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); if (config.allowedKeyManagement.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.KeyMgmt.varName, - allowedKeyManagementString)) { + !mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.KeyMgmt.varName, + allowedKeyManagementString)) { if (DBG) { - Log.d(TAG, "failed to set key_mgmt: "+ - allowedKeyManagementString); + Slog.d(TAG, "failed to set key_mgmt: "+ + allowedKeyManagementString); } break setVariables; } @@ -779,13 +1097,13 @@ public class WifiService extends IWifiManager.Stub { String allowedProtocolsString = makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); if (config.allowedProtocols.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.Protocol.varName, - allowedProtocolsString)) { + !mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.Protocol.varName, + allowedProtocolsString)) { if (DBG) { - Log.d(TAG, "failed to set proto: "+ - allowedProtocolsString); + Slog.d(TAG, "failed to set proto: "+ + allowedProtocolsString); } break setVariables; } @@ -793,13 +1111,13 @@ public class WifiService extends IWifiManager.Stub { String allowedAuthAlgorithmsString = makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); if (config.allowedAuthAlgorithms.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.AuthAlgorithm.varName, - allowedAuthAlgorithmsString)) { + !mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.AuthAlgorithm.varName, + allowedAuthAlgorithmsString)) { if (DBG) { - Log.d(TAG, "failed to set auth_alg: "+ - allowedAuthAlgorithmsString); + Slog.d(TAG, "failed to set auth_alg: "+ + allowedAuthAlgorithmsString); } break setVariables; } @@ -807,13 +1125,13 @@ public class WifiService extends IWifiManager.Stub { String allowedPairwiseCiphersString = makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings); if (config.allowedPairwiseCiphers.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.PairwiseCipher.varName, - allowedPairwiseCiphersString)) { + !mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.PairwiseCipher.varName, + allowedPairwiseCiphersString)) { if (DBG) { - Log.d(TAG, "failed to set pairwise: "+ - allowedPairwiseCiphersString); + Slog.d(TAG, "failed to set pairwise: "+ + allowedPairwiseCiphersString); } break setVariables; } @@ -821,13 +1139,13 @@ public class WifiService extends IWifiManager.Stub { String allowedGroupCiphersString = makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); if (config.allowedGroupCiphers.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.GroupCipher.varName, - allowedGroupCiphersString)) { + !mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.GroupCipher.varName, + allowedGroupCiphersString)) { if (DBG) { - Log.d(TAG, "failed to set group: "+ - allowedGroupCiphersString); + Slog.d(TAG, "failed to set group: "+ + allowedGroupCiphersString); } break setVariables; } @@ -835,12 +1153,12 @@ public class WifiService extends IWifiManager.Stub { // Prevent client screw-up by passing in a WifiConfiguration we gave it // by preventing "*" as a key. if (config.preSharedKey != null && !config.preSharedKey.equals("*") && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.pskVarName, - config.preSharedKey)) { + !mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.pskVarName, + config.preSharedKey)) { if (DBG) { - Log.d(TAG, "failed to set psk: "+config.preSharedKey); + Slog.d(TAG, "failed to set psk: "+config.preSharedKey); } break setVariables; } @@ -851,14 +1169,14 @@ public class WifiService extends IWifiManager.Stub { // Prevent client screw-up by passing in a WifiConfiguration we gave it // by preventing "*" as a key. if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { - if (!WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.wepKeyVarNames[i], - config.wepKeys[i])) { + if (!mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.wepKeyVarNames[i], + config.wepKeys[i])) { if (DBG) { - Log.d(TAG, - "failed to set wep_key"+i+": " + - config.wepKeys[i]); + Slog.d(TAG, + "failed to set wep_key"+i+": " + + config.wepKeys[i]); } break setVariables; } @@ -868,37 +1186,37 @@ public class WifiService extends IWifiManager.Stub { } if (hasSetKey) { - if (!WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.wepTxKeyIdxVarName, - Integer.toString(config.wepTxKeyIndex))) { + if (!mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.wepTxKeyIdxVarName, + Integer.toString(config.wepTxKeyIndex))) { if (DBG) { - Log.d(TAG, - "failed to set wep_tx_keyidx: "+ - config.wepTxKeyIndex); + Slog.d(TAG, + "failed to set wep_tx_keyidx: "+ + config.wepTxKeyIndex); } break setVariables; } } - if (!WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.priorityVarName, - Integer.toString(config.priority))) { + if (!mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.priorityVarName, + Integer.toString(config.priority))) { if (DBG) { - Log.d(TAG, config.SSID + ": failed to set priority: " - +config.priority); + Slog.d(TAG, config.SSID + ": failed to set priority: " + +config.priority); } break setVariables; } - if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.hiddenSSIDVarName, - Integer.toString(config.hiddenSSID ? 1 : 0))) { + if (config.hiddenSSID && !mWifiStateTracker.setNetworkVariable( + netId, + WifiConfiguration.hiddenSSIDVarName, + Integer.toString(config.hiddenSSID ? 1 : 0))) { if (DBG) { - Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+ - config.hiddenSSID); + Slog.d(TAG, config.SSID + ": failed to set hiddenSSID: "+ + config.hiddenSSID); } break setVariables; } @@ -907,18 +1225,22 @@ public class WifiService extends IWifiManager.Stub { : config.enterpriseFields) { String varName = field.varName(); String value = field.value(); - if ((value != null) && !WifiNative.setNetworkVariableCommand( - netId, - varName, - value)) { - if (DBG) { - Log.d(TAG, config.SSID + ": failed to set " + varName + - ": " + value); + if (value != null) { + if (field != config.eap) { + value = (value.length() == 0) ? "NULL" : convertToQuotedString(value); + } + if (!mWifiStateTracker.setNetworkVariable( + netId, + varName, + value)) { + if (DBG) { + Slog.d(TAG, config.SSID + ": failed to set " + varName + + ": " + value); + } + break setVariables; } - break setVariables; } } - return netId; } @@ -931,9 +1253,9 @@ public class WifiService extends IWifiManager.Stub { if (newNetwork) { removeNetwork(netId); if (DBG) { - Log.d(TAG, - "Failed to set a network variable, removed network: " - + netId); + Slog.d(TAG, + "Failed to set a network variable, removed network: " + + netId); } } return -1; @@ -972,7 +1294,7 @@ public class WifiService extends IWifiManager.Stub { // if we ever get here, we should probably add the // value to WifiConfiguration to reflect that it's // supported by the WPA supplicant - Log.w(TAG, "Failed to look-up a string: " + string); + Slog.w(TAG, "Failed to look-up a string: " + string); } return -1; @@ -1000,15 +1322,13 @@ public class WifiService extends IWifiManager.Stub { public boolean enableNetwork(int netId, boolean disableOthers) { enforceChangePermission(); - synchronized (mWifiStateTracker) { - String ifname = mWifiStateTracker.getInterfaceName(); - NetworkUtils.enableInterface(ifname); - boolean result = WifiNative.enableNetworkCommand(netId, disableOthers); - if (!result) { - NetworkUtils.disableInterface(ifname); - } - return result; + String ifname = mWifiStateTracker.getInterfaceName(); + NetworkUtils.enableInterface(ifname); + boolean result = mWifiStateTracker.enableNetwork(netId, disableOthers); + if (!result) { + NetworkUtils.disableInterface(ifname); } + return result; } /** @@ -1020,9 +1340,7 @@ public class WifiService extends IWifiManager.Stub { public boolean disableNetwork(int netId) { enforceChangePermission(); - synchronized (mWifiStateTracker) { - return WifiNative.disableNetworkCommand(netId); - } + return mWifiStateTracker.disableNetwork(netId); } /** @@ -1046,9 +1364,8 @@ public class WifiService extends IWifiManager.Stub { public List<ScanResult> getScanResults() { enforceAccessPermission(); String reply; - synchronized (mWifiStateTracker) { - reply = WifiNative.scanResultsCommand(); - } + + reply = mWifiStateTracker.scanResults(); if (reply == null) { return null; } @@ -1076,7 +1393,7 @@ public class WifiService extends IWifiManager.Stub { if (scanResult != null) { scanList.add(scanResult); } else if (DBG) { - Log.w(TAG, "misformatted scan result for: " + line); + Slog.w(TAG, "misformatted scan result for: " + line); } } lineBeg = lineEnd + 1; @@ -1163,7 +1480,7 @@ public class WifiService extends IWifiManager.Stub { } } } else { - Log.w(TAG, "Misformatted scan result text with " + + Slog.w(TAG, "Misformatted scan result text with " + result.length + " fields: " + line); } } @@ -1196,11 +1513,12 @@ public class WifiService extends IWifiManager.Stub { public boolean saveConfiguration() { boolean result; enforceChangePermission(); + synchronized (mWifiStateTracker) { - result = WifiNative.saveConfigCommand(); + result = mWifiStateTracker.saveConfig(); if (result && mNeedReconfig) { mNeedReconfig = false; - result = WifiNative.reloadConfigCommand(); + result = mWifiStateTracker.reloadConfig(); if (result) { Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION); @@ -1234,9 +1552,10 @@ public class WifiService extends IWifiManager.Stub { * {@code numChannels} is outside the valid range. */ public boolean setNumAllowedChannels(int numChannels, boolean persist) { - Log.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+ + Slog.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+ " with persist set to "+persist); enforceChangePermission(); + /* * Validate the argument. We'd like to let the Wi-Fi driver do this, * but if Wi-Fi isn't currently enabled, that's not possible, and @@ -1254,13 +1573,28 @@ public class WifiService extends IWifiManager.Stub { return false; } + if (mWifiHandler == null) return false; + + Message.obtain(mWifiHandler, + MESSAGE_SET_CHANNELS, numChannels, (persist ? 1 : 0)).sendToTarget(); + + return true; + } + + /** + * sets the number of allowed radio frequency channels synchronously + * @param numChannels the number of allowed channels. Must be greater than 0 + * and less than or equal to 16. + * @param persist {@code true} if the setting should be remembered. + * @return {@code true} if the operation succeeds, {@code false} otherwise + */ + private boolean setNumAllowedChannelsBlocking(int numChannels, boolean persist) { if (persist) { Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, - numChannels); + Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, + numChannels); } - mWifiStateTracker.setNumAllowedChannels(numChannels); - return true; + return mWifiStateTracker.setNumAllowedChannels(numChannels); } /** @@ -1272,18 +1606,17 @@ public class WifiService extends IWifiManager.Stub { int numChannels; enforceAccessPermission(); - synchronized (mWifiStateTracker) { - /* - * If we can't get the value from the driver (e.g., because - * Wi-Fi is not currently enabled), get the value from - * Settings. - */ - numChannels = WifiNative.getNumAllowedChannelsCommand(); - if (numChannels < 0) { - numChannels = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, - -1); - } + + /* + * If we can't get the value from the driver (e.g., because + * Wi-Fi is not currently enabled), get the value from + * Settings. + */ + numChannels = mWifiStateTracker.getNumAllowedChannels(); + if (numChannels < 0) { + numChannels = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, + -1); } return numChannels; } @@ -1313,19 +1646,20 @@ public class WifiService extends IWifiManager.Stub { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - long idleMillis = Settings.Gservices.getLong(mContext.getContentResolver(), - Settings.Gservices.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); + long idleMillis = + Settings.Secure.getLong(mContext.getContentResolver(), + Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); int stayAwakeConditions = - Settings.System.getInt(mContext.getContentResolver(), - Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); + Settings.System.getInt(mContext.getContentResolver(), + Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); if (action.equals(Intent.ACTION_SCREEN_ON)) { - Log.d(TAG, "ACTION_SCREEN_ON"); + Slog.d(TAG, "ACTION_SCREEN_ON"); mAlarmManager.cancel(mIdleIntent); mDeviceIdle = false; mScreenOff = false; mWifiStateTracker.enableRssiPolling(true); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { - Log.d(TAG, "ACTION_SCREEN_OFF"); + Slog.d(TAG, "ACTION_SCREEN_OFF"); mScreenOff = true; mWifiStateTracker.enableRssiPolling(false); /* @@ -1342,21 +1676,21 @@ public class WifiService extends IWifiManager.Stub { // as long as we would if connected (below) // TODO - fix the race conditions and switch back to the immediate turn-off long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min - Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms"); + Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms"); mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); // // do not keep Wifi awake when screen is off if Wifi is not associated // mDeviceIdle = true; // updateWifiState(); } else { long triggerTime = System.currentTimeMillis() + idleMillis; - Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms"); + Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms"); mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); } } /* we can return now -- there's nothing to do until we get the idle intent back */ return; } else if (action.equals(ACTION_DEVICE_IDLE)) { - Log.d(TAG, "got ACTION_DEVICE_IDLE"); + Slog.d(TAG, "got ACTION_DEVICE_IDLE"); mDeviceIdle = true; } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { /* @@ -1367,11 +1701,11 @@ public class WifiService extends IWifiManager.Stub { * the already-set timer. */ int pluggedType = intent.getIntExtra("plugged", 0); - Log.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType); + Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType); if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) && !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) { long triggerTime = System.currentTimeMillis() + idleMillis; - Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms"); + Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms"); mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); mPluggedType = pluggedType; return; @@ -1449,7 +1783,18 @@ public class WifiService extends IWifiManager.Stub { Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget(); } + private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) { + Message.obtain(mWifiHandler, + (enable ? MESSAGE_START_ACCESS_POINT : MESSAGE_STOP_ACCESS_POINT), + uid, 0, wifiConfig).sendToTarget(); + } + private void updateWifiState() { + // send a message so it's all serialized + Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget(); + } + + private void doUpdateWifiState() { boolean wifiEnabled = getPersistedWifiEnabled(); boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden; boolean lockHeld = mLocks.hasLocks(); @@ -1463,29 +1808,33 @@ public class WifiService extends IWifiManager.Stub { } synchronized (mWifiHandler) { - if (mWifiState == WIFI_STATE_ENABLING && !airplaneMode) { + if ((mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLING) && !airplaneMode) { return; } + + /* Disable tethering when airplane mode is enabled */ + if (airplaneMode && + (mWifiApState == WIFI_AP_STATE_ENABLING || mWifiApState == WIFI_AP_STATE_ENABLED)) { + sWakeLock.acquire(); + sendAccessPointMessage(false, null, mLastApEnableUid); + } + if (wifiShouldBeEnabled) { if (wifiShouldBeStarted) { sWakeLock.acquire(); sendEnableMessage(true, false, mLastEnableUid); sWakeLock.acquire(); sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY); - } else { + } else if (!mWifiStateTracker.isDriverStopped()) { int wakeLockTimeout = Settings.Secure.getInt( 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. + * We are assuming that ConnectivityService can make + * a transition to cellular data within wakeLockTimeout time. + * The wakelock is released by the delayed message. */ sDriverStopWakeLock.acquire(); mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI); @@ -1546,6 +1895,9 @@ public class WifiService extends IWifiManager.Stub { case MESSAGE_ENABLE_WIFI: setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2); + if (mWifiWatchdogService == null) { + mWifiWatchdogService = new WifiWatchdogService(mContext, mWifiStateTracker); + } sWakeLock.release(); break; @@ -1555,10 +1907,15 @@ public class WifiService extends IWifiManager.Stub { sWakeLock.release(); break; + case MESSAGE_UPDATE_STATE: + doUpdateWifiState(); + break; + case MESSAGE_DISABLE_WIFI: // a non-zero msg.arg1 value means the "enabled" setting // should be persisted setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2); + mWifiWatchdogService = null; sWakeLock.release(); break; @@ -1568,12 +1925,27 @@ public class WifiService extends IWifiManager.Stub { break; case MESSAGE_RELEASE_WAKELOCK: - synchronized (sDriverStopWakeLock) { - if (sDriverStopWakeLock.isHeld()) { - sDriverStopWakeLock.release(); - } - } + sDriverStopWakeLock.release(); break; + + case MESSAGE_START_ACCESS_POINT: + setWifiApEnabledBlocking(true, + msg.arg1, + (WifiConfiguration) msg.obj); + sWakeLock.release(); + break; + + case MESSAGE_STOP_ACCESS_POINT: + setWifiApEnabledBlocking(false, + msg.arg1, + (WifiConfiguration) msg.obj); + sWakeLock.release(); + break; + + case MESSAGE_SET_CHANNELS: + setNumAllowedChannelsBlocking(msg.arg1, msg.arg2 == 1); + break; + } } } @@ -1587,7 +1959,7 @@ public class WifiService extends IWifiManager.Stub { + ", uid=" + Binder.getCallingUid()); return; } - pw.println("Wi-Fi is " + stateName(mWifiState)); + pw.println("Wi-Fi is " + stateName(mWifiStateTracker.getWifiState())); pw.println("Stay-awake conditions: " + Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0)); @@ -1720,7 +2092,7 @@ public class WifiService extends IWifiManager.Stub { } private boolean acquireWifiLockLocked(WifiLock wifiLock) { - Log.d(TAG, "acquireWifiLockLocked: " + wifiLock); + Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock); mLocks.addLock(wifiLock); @@ -1758,7 +2130,7 @@ public class WifiService extends IWifiManager.Stub { WifiLock wifiLock = mLocks.removeLock(lock); - Log.d(TAG, "releaseWifiLockLocked: " + wifiLock); + Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock); hadLock = (wifiLock != null); @@ -1815,7 +2187,7 @@ public class WifiService extends IWifiManager.Stub { } public void binderDied() { - Log.e(TAG, "Multicaster binderDied"); + Slog.e(TAG, "Multicaster binderDied"); synchronized (mMulticasters) { int i = mMulticasters.indexOf(this); if (i != -1) { @@ -1833,6 +2205,19 @@ public class WifiService extends IWifiManager.Stub { } } + public void initializeMulticastFiltering() { + enforceMulticastChangePermission(); + + synchronized (mMulticasters) { + // if anybody had requested filters be off, leave off + if (mMulticasters.size() != 0) { + return; + } else { + mWifiStateTracker.startPacketFiltering(); + } + } + } + public void acquireMulticastLock(IBinder binder, String tag) { enforceMulticastChangePermission(); @@ -1843,9 +2228,7 @@ public class WifiService extends IWifiManager.Stub { // our new size == 1 (first call), but this function won't // be called often and by making the stopPacket call each // time we're less fragile and self-healing. - synchronized (mWifiStateTracker) { - WifiNative.stopPacketFiltering(); - } + mWifiStateTracker.stopPacketFiltering(); } int uid = Binder.getCallingUid(); @@ -1877,13 +2260,12 @@ public class WifiService extends IWifiManager.Stub { private void removeMulticasterLocked(int i, int uid) { Multicaster removed = mMulticasters.remove(i); + if (removed != null) { removed.unlinkDeathRecipient(); } if (mMulticasters.size() == 0) { - synchronized (mWifiStateTracker) { - WifiNative.startPacketFiltering(); - } + mWifiStateTracker.startPacketFiltering(); } Long ident = Binder.clearCallingIdentity(); diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java index 9443a95..87f8a6e 100644 --- a/services/java/com/android/server/WifiWatchdogService.java +++ b/services/java/com/android/server/WifiWatchdogService.java @@ -34,7 +34,7 @@ import android.os.Message; import android.provider.Settings; import android.text.TextUtils; import android.util.Config; -import android.util.Log; +import android.util.Slog; import java.io.IOException; import java.net.DatagramPacket; @@ -89,6 +89,8 @@ public class WifiWatchdogService { */ private WifiWatchdogHandler mHandler; + private ContentObserver mContentObserver; + /** * The current watchdog state. Only written from the main thread! */ @@ -132,7 +134,7 @@ public class WifiWatchdogService { ContentResolver contentResolver = mContext.getContentResolver(); contentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON), false, - new ContentObserver(mHandler) { + mContentObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { if (isWatchdogEnabled()) { @@ -249,7 +251,6 @@ public class WifiWatchdogService { private void registerForWifiBroadcasts() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - intentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); mContext.registerReceiver(mReceiver, intentFilter); } @@ -272,6 +273,16 @@ public class WifiWatchdogService { } /** + * Unregister broadcasts and quit the watchdog thread + */ + private void quit() { + unregisterForWifiBroadcasts(); + mContext.getContentResolver().unregisterContentObserver(mContentObserver); + mHandler.removeAllActions(); + mHandler.getLooper().quit(); + } + + /** * Waits for the main watchdog thread to create the handler. */ private void waitForHandlerCreation() { @@ -281,7 +292,7 @@ public class WifiWatchdogService { // Wait for the handler to be set by the other thread wait(); } catch (InterruptedException e) { - Log.e(TAG, "Interrupted while waiting on handler."); + Slog.e(TAG, "Interrupted while waiting on handler."); } } } @@ -293,11 +304,11 @@ public class WifiWatchdogService { * Logs with the current thread. */ private static void myLogV(String message) { - Log.v(TAG, "(" + Thread.currentThread().getName() + ") " + message); + Slog.v(TAG, "(" + Thread.currentThread().getName() + ") " + message); } private static void myLogD(String message) { - Log.d(TAG, "(" + Thread.currentThread().getName() + ") " + message); + Slog.d(TAG, "(" + Thread.currentThread().getName() + ") " + message); } /** @@ -364,7 +375,7 @@ public class WifiWatchdogService { } if (V) { - Log.v(TAG, (dnsAlive ? " +" : " Ignored: -")); + Slog.v(TAG, (dnsAlive ? " +" : " Ignored: -")); } if (shouldCancel()) return false; @@ -372,7 +383,7 @@ public class WifiWatchdogService { try { Thread.sleep(pingDelay); } catch (InterruptedException e) { - Log.w(TAG, "Interrupted while pausing between pings", e); + Slog.w(TAG, "Interrupted while pausing between pings", e); } } @@ -383,11 +394,11 @@ public class WifiWatchdogService { if (DnsPinger.isDnsReachable(dns, getPingTimeoutMs())) { successCounter++; if (V) { - Log.v(TAG, " +"); + Slog.v(TAG, " +"); } } else { if (V) { - Log.v(TAG, " -"); + Slog.v(TAG, " -"); } } @@ -396,13 +407,13 @@ public class WifiWatchdogService { try { Thread.sleep(pingDelay); } catch (InterruptedException e) { - Log.w(TAG, "Interrupted while pausing between pings", e); + Slog.w(TAG, "Interrupted while pausing between pings", e); } } int packetLossPercentage = 100 * (numPings - successCounter) / numPings; if (D) { - Log.d(TAG, packetLossPercentage + Slog.d(TAG, packetLossPercentage + "% packet loss (acceptable is " + acceptableLoss + "%)"); } @@ -544,7 +555,7 @@ public class WifiWatchdogService { if (ssid == null) { // It's still null, give up if (V) { - Log.v(TAG, " Invalid SSID, returning false"); + Slog.v(TAG, " Invalid SSID, returning false"); } return false; } @@ -559,7 +570,7 @@ public class WifiWatchdogService { if (TextUtils.isEmpty(bssid)) { // It's still null, give up if (V) { - Log.v(TAG, " Invalid BSSID, returning false"); + Slog.v(TAG, " Invalid BSSID, returning false"); } return false; } @@ -567,7 +578,7 @@ public class WifiWatchdogService { if (!isOnWatchList(ssid)) { if (V) { - Log.v(TAG, " SSID not on watch list, returning false"); + Slog.v(TAG, " SSID not on watch list, returning false"); } return false; } @@ -667,7 +678,7 @@ public class WifiWatchdogService { // Make sure we are not sleeping if (mState == WatchdogState.SLEEP) { if (V) { - Log.v(TAG, " Sleeping (in " + mSsid + "), so returning"); + Slog.v(TAG, " Sleeping (in " + mSsid + "), so returning"); } return; } @@ -681,7 +692,7 @@ public class WifiWatchdogService { mNumApsChecked++; if (mNumApsChecked > getMaxApChecks()) { if (V) { - Log.v(TAG, " Passed the max attempts (" + getMaxApChecks() + Slog.v(TAG, " Passed the max attempts (" + getMaxApChecks() + "), going to sleep for " + mSsid); } mHandler.sleep(mSsid); @@ -692,7 +703,7 @@ public class WifiWatchdogService { boolean isApAlive = checkDnsConnectivity(); if (V) { - Log.v(TAG, " Is it alive: " + isApAlive); + Slog.v(TAG, " Is it alive: " + isApAlive); } // Take action based on results @@ -739,6 +750,8 @@ public class WifiWatchdogService { // Black list this "bad" AP, this will cause an attempt to connect to another blacklistAp(ap.bssid); + // Initiate an association to an alternate AP + mWifiStateTracker.reassociate(); } private void blacklistAp(String bssid) { @@ -751,7 +764,7 @@ public class WifiWatchdogService { if (!mWifiStateTracker.addToBlacklist(bssid)) { // There's a known bug where this method returns failure on success - //Log.e(TAG, "Blacklisting " + bssid + " failed"); + //Slog.e(TAG, "Blacklisting " + bssid + " failed"); } if (D) { @@ -778,7 +791,7 @@ public class WifiWatchdogService { // Make sure we are not sleeping if (mState == WatchdogState.SLEEP) { if (V) { - Log.v(TAG, " handleBackgroundCheckAp: Sleeping (in " + mSsid + "), so returning"); + Slog.v(TAG, " handleBackgroundCheckAp: Sleeping (in " + mSsid + "), so returning"); } return; } @@ -805,7 +818,7 @@ public class WifiWatchdogService { boolean isApAlive = backgroundCheckDnsConnectivity(); if (V && !isApAlive) { - Log.v(TAG, " handleBackgroundCheckAp: Is it alive: " + isApAlive); + Slog.v(TAG, " handleBackgroundCheckAp: Is it alive: " + isApAlive); } if (shouldCancel()) { @@ -849,7 +862,7 @@ public class WifiWatchdogService { */ if (!mWifiStateTracker.clearBlacklist()) { // There's a known bug where this method returns failure on success - //Log.e(TAG, "Clearing blacklist failed"); + //Slog.e(TAG, "Clearing blacklist failed"); } if (V) { @@ -893,7 +906,7 @@ public class WifiWatchdogService { // If we're sleeping, don't do anything if (mState == WatchdogState.SLEEP) { - Log.v(TAG, " Sleeping (in " + mSsid + "), so returning"); + Slog.v(TAG, " Sleeping (in " + mSsid + "), so returning"); return; } @@ -901,7 +914,7 @@ public class WifiWatchdogService { setIdleState(false); if (V) { - Log.v(TAG, " Set state to IDLE"); + Slog.v(TAG, " Set state to IDLE"); } } @@ -1103,9 +1116,6 @@ public class WifiWatchdogService { if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { handleNetworkStateChanged( (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO)); - } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) { - handleSupplicantConnectionChanged( - intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)); } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { handleWifiStateChanged(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN)); @@ -1139,15 +1149,9 @@ public class WifiWatchdogService { } } - private void handleSupplicantConnectionChanged(boolean connected) { - if (!connected) { - onDisconnected(); - } - } - private void handleWifiStateChanged(int wifiState) { if (wifiState == WifiManager.WIFI_STATE_DISABLED) { - onDisconnected(); + quit(); } else if (wifiState == WifiManager.WIFI_STATE_ENABLED) { onEnabled(); } @@ -1243,13 +1247,13 @@ public class WifiWatchdogService { } catch (SocketException e) { if (V) { - Log.v(TAG, "DnsPinger.isReachable received SocketException", e); + Slog.v(TAG, "DnsPinger.isReachable received SocketException", e); } return false; } catch (UnknownHostException e) { if (V) { - Log.v(TAG, "DnsPinger.isReachable is unable to resolve the DNS host", e); + Slog.v(TAG, "DnsPinger.isReachable is unable to resolve the DNS host", e); } return false; @@ -1258,13 +1262,13 @@ public class WifiWatchdogService { } catch (IOException e) { if (V) { - Log.v(TAG, "DnsPinger.isReachable got an IOException", e); + Slog.v(TAG, "DnsPinger.isReachable got an IOException", e); } return false; } catch (Exception e) { if (V || Config.LOGD) { - Log.d(TAG, "DnsPinger.isReachable got an unknown exception", e); + Slog.d(TAG, "DnsPinger.isReachable got an unknown exception", e); } return false; } diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index d209cfa..205e308 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -43,9 +43,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import com.android.internal.app.IBatteryStats; import com.android.internal.policy.PolicyManager; +import com.android.internal.policy.impl.PhoneWindowManager; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; import com.android.internal.view.IInputMethodManager; +import com.android.internal.view.WindowManagerPolicyThread; import com.android.server.KeyInputQueue.QueuedEvent; import com.android.server.am.BatteryStatsService; @@ -84,7 +86,7 @@ import android.os.TokenWatcher; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.EventLog; -import android.util.Log; +import android.util.Slog; import android.util.SparseIntArray; import android.view.Display; import android.view.Gravity; @@ -134,16 +136,19 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean DEBUG_FOCUS = false; static final boolean DEBUG_ANIM = false; static final boolean DEBUG_LAYOUT = false; + static final boolean DEBUG_RESIZE = 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_WINDOW_MOVEMENT = false; static final boolean DEBUG_ORIENTATION = false; + static final boolean DEBUG_CONFIGURATION = false; static final boolean DEBUG_APP_TRANSITIONS = false; static final boolean DEBUG_STARTING_WINDOW = false; static final boolean DEBUG_REORDER = false; static final boolean DEBUG_WALLPAPER = false; + static final boolean DEBUG_FREEZE = false; static final boolean SHOW_TRANSACTIONS = false; static final boolean HIDE_STACK_CRAWLS = true; static final boolean MEASURE_LATENCY = false; @@ -153,8 +158,6 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean BLUR = true; static final boolean localLOGV = DEBUG; - static final int LOG_WM_NO_SURFACE_MEMORY = 31000; - /** How long to wait for subsequent key repeats, in milliseconds */ static final int KEY_REPEAT_DELAY = 50; @@ -194,7 +197,7 @@ public class WindowManagerService extends IWindowManager.Stub static final int INJECT_FAILED = 0; static final int INJECT_SUCCEEDED = 1; static final int INJECT_NO_PERMISSION = -1; - + static final int UPDATE_FOCUS_NORMAL = 0; static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; static final int UPDATE_FOCUS_PLACING_SURFACES = 2; @@ -310,13 +313,13 @@ public class WindowManagerService extends IWindowManager.Stub * animation. It will be used for the next exit animation. */ AppWindowToken mLastEnterAnimToken; - + /** * These were the layout params used to retrieve the last enter animation. * They will be used for the next exit animation. */ LayoutParams mLastEnterAnimParams; - + /** * Z-ordered (bottom-most first) list of all Window objects. */ @@ -365,6 +368,8 @@ public class WindowManagerService extends IWindowManager.Stub boolean mSafeMode; boolean mDisplayEnabled = false; boolean mSystemBooted = false; + int mInitialDisplayWidth = 0; + int mInitialDisplayHeight = 0; int mRotation = 0; int mRequestedRotation = 0; int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -375,10 +380,18 @@ public class WindowManagerService extends IWindowManager.Stub boolean mLayoutNeeded = true; boolean mAnimationPending = false; boolean mDisplayFrozen = false; + boolean mWaitingForConfig = false; boolean mWindowsFreezingScreen = false; long mFreezeGcPending = 0; int mAppsFreezingScreen = 0; + int mLayoutSeq = 0; + + // State while inside of layoutAndPlaceSurfacesLocked(). + boolean mFocusMayChange; + + Configuration mCurConfiguration = new Configuration(); + // 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. @@ -423,7 +436,7 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>(); final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>(); - + // If non-null, this is the currently visible window that is associated // with the wallpaper. WindowState mWallpaperTarget = null; @@ -448,7 +461,7 @@ public class WindowManagerService extends IWindowManager.Stub static final long WALLPAPER_TIMEOUT = 150; // Time we wait after a timeout before trying to wait again. static final long WALLPAPER_TIMEOUT_RECOVERY = 10000; - + AppWindowToken mFocusedApp = null; PowerManagerService mPowerManager; @@ -464,7 +477,7 @@ public class WindowManagerService extends IWindowManager.Stub Session mHoldingScreenOn; boolean mTurnOnScreen; - + /** * Whether the UI is currently running in touch mode (not showing * navigational focus because the user is directly pressing the screen). @@ -551,8 +564,10 @@ public class WindowManagerService extends IWindowManager.Stub public void run() { Looper.prepare(); + WindowManagerPolicyThread.set(this, Looper.myLooper()); + //Looper.myLooper().setMessageLogging(new LogPrinter( - // Log.VERBOSE, "WindowManagerPolicy")); + // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM)); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); mPolicy.init(mContext, mService, mPM); @@ -571,7 +586,7 @@ public class WindowManagerService extends IWindowManager.Stub if (MEASURE_LATENCY) { lt = new LatencyTimer(100, 1000); } - + mContext = context; mHaveInputMethods = haveInputMethods; mLimitedAlphaCompositing = context.getResources().getBoolean( @@ -635,7 +650,7 @@ public class WindowManagerService extends IWindowManager.Stub // The window manager only throws security exceptions, so let's // log all others. if (!(e instanceof SecurityException)) { - Log.e(TAG, "Window Manager Crash", e); + Slog.e(TAG, "Window Manager Crash", e); } throw e; } @@ -643,7 +658,7 @@ public class WindowManagerService extends IWindowManager.Stub private void placeWindowAfter(Object pos, WindowState window) { final int i = mWindows.indexOf(pos); - if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v( TAG, "Adding window " + window + " at " + (i+1) + " of " + mWindows.size() + " (after " + pos + ")"); mWindows.add(i+1, window); @@ -651,7 +666,7 @@ public class WindowManagerService extends IWindowManager.Stub private void placeWindowBefore(Object pos, WindowState window) { final int i = mWindows.indexOf(pos); - if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v( TAG, "Adding window " + window + " at " + i + " of " + mWindows.size() + " (before " + pos + ")"); mWindows.add(i, window); @@ -708,7 +723,7 @@ public class WindowManagerService extends IWindowManager.Stub //apptoken note that the window could be a floating window //that was created later or a window at the top of the list of //windows associated with this token. - if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v( TAG, "Adding window " + win + " at " + (newIdx+1) + " of " + N); localmWindows.add(newIdx+1, win); @@ -716,7 +731,7 @@ public class WindowManagerService extends IWindowManager.Stub } } } else { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Figuring out where to add app window " + client.asBinder() + " (token=" + token + ")"); // Figure out where the window should go, based on the @@ -729,7 +744,7 @@ public class WindowManagerService extends IWindowManager.Stub i--; break; } - + // We haven't reached the token yet; if this token // is not going to the bottom and has windows, we can // use it as an anchor for when we do reach the token. @@ -790,7 +805,7 @@ public class WindowManagerService extends IWindowManager.Stub break; } } - if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v( TAG, "Adding window " + win + " at " + i + " of " + N); localmWindows.add(i, win); @@ -807,7 +822,7 @@ public class WindowManagerService extends IWindowManager.Stub } } if (i < 0) i = 0; - if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v( TAG, "Adding window " + win + " at " + i + " of " + N); localmWindows.add(i, win); @@ -891,10 +906,10 @@ public class WindowManagerService extends IWindowManager.Stub i--; w = (WindowState)localmWindows.get(i); - //Log.i(TAG, "Checking window @" + i + " " + w + " fl=0x" + //Slog.i(TAG, "Checking window @" + i + " " + w + " fl=0x" // + Integer.toHexString(w.mAttrs.flags)); if (canBeImeTarget(w)) { - //Log.i(TAG, "Putting input method here!"); + //Slog.i(TAG, "Putting input method here!"); // Yet more tricksyness! If this window is a "starting" // window, we do actually want to be on top of it, but @@ -916,7 +931,7 @@ public class WindowManagerService extends IWindowManager.Stub mUpcomingInputMethodTarget = w; - if (DEBUG_INPUT_METHOD) Log.v(TAG, "Desired input method target=" + if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Desired input method target=" + w + " willMove=" + willMove); if (willMove && w != null) { @@ -950,7 +965,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (highestTarget != null) { - if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" + if (DEBUG_INPUT_METHOD) Slog.v(TAG, "mNextAppTransition=" + mNextAppTransition + " " + highestTarget + " animating=" + highestTarget.isAnimating() + " layer=" + highestTarget.mAnimLayer @@ -975,13 +990,18 @@ public class WindowManagerService extends IWindowManager.Stub } } - //Log.i(TAG, "Placing input method @" + (i+1)); + //Slog.i(TAG, "Placing input method @" + (i+1)); if (w != null) { if (willMove) { - RuntimeException e = new RuntimeException(); - if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); - if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from " - + mInputMethodTarget + " to " + w, e); + if (DEBUG_INPUT_METHOD) { + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.w(TAG, "Moving IM target from " + + mInputMethodTarget + " to " + w, e); + } mInputMethodTarget = w; if (w.mAppToken != null) { setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment); @@ -992,10 +1012,15 @@ public class WindowManagerService extends IWindowManager.Stub return i+1; } if (willMove) { - RuntimeException e = new RuntimeException(); - if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); - if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from " - + mInputMethodTarget + " to null", e); + if (DEBUG_INPUT_METHOD) { + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.w(TAG, "Moving IM target from " + + mInputMethodTarget + " to null", e); + } mInputMethodTarget = null; setInputMethodAnimLayerAdjustment(0); } @@ -1006,7 +1031,7 @@ public class WindowManagerService extends IWindowManager.Stub int pos = findDesiredInputMethodWindowIndexLocked(true); if (pos >= 0) { win.mTargetAppToken = mInputMethodTarget.mAppToken; - if (DEBUG_WINDOW_MOVEMENT) Log.v( + if (DEBUG_WINDOW_MOVEMENT) Slog.v( TAG, "Adding input method window " + win + " at " + pos); mWindows.add(pos, win); moveInputMethodDialogsLocked(pos+1); @@ -1018,19 +1043,19 @@ public class WindowManagerService extends IWindowManager.Stub } void setInputMethodAnimLayerAdjustment(int adj) { - if (DEBUG_LAYERS) Log.v(TAG, "Setting im layer adj to " + adj); + if (DEBUG_LAYERS) Slog.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 + if (DEBUG_LAYERS) Slog.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 + if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + cw + " anim layer: " + cw.mAnimLayer); } } @@ -1039,7 +1064,7 @@ public class WindowManagerService extends IWindowManager.Stub di --; imw = mInputMethodDialogs.get(di); imw.mAnimLayer = imw.mLayer + adj; - if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw + if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + imw + " anim layer: " + imw.mAnimLayer); } } @@ -1048,7 +1073,7 @@ public class WindowManagerService extends IWindowManager.Stub int wpos = mWindows.indexOf(win); if (wpos >= 0) { if (wpos < interestingPos) interestingPos--; - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Temp removing at " + wpos + ": " + win); + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing at " + wpos + ": " + win); mWindows.remove(wpos); int NC = win.mChildWindows.size(); while (NC > 0) { @@ -1057,7 +1082,7 @@ public class WindowManagerService extends IWindowManager.Stub int cpos = mWindows.indexOf(cw); if (cpos >= 0) { if (cpos < interestingPos) interestingPos--; - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Temp removing child at " + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing child at " + cpos + ": " + cw); mWindows.remove(cpos); } @@ -1073,7 +1098,7 @@ public class WindowManagerService extends IWindowManager.Stub // this case should be rare, so it shouldn't be that big a deal. int wpos = mWindows.indexOf(win); if (wpos >= 0) { - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "ReAdd removing from " + wpos + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "ReAdd removing from " + wpos + ": " + win); mWindows.remove(wpos); reAddWindowLocked(wpos, win); @@ -1084,7 +1109,7 @@ public class WindowManagerService extends IWindowManager.Stub int N = mWindows.size(); while (N > 0) { N--; - Log.v(TAG, prefix + "#" + N + ": " + mWindows.get(N)); + Slog.v(TAG, prefix + "#" + N + ": " + mWindows.get(N)); } } @@ -1092,12 +1117,12 @@ public class WindowManagerService extends IWindowManager.Stub ArrayList<WindowState> dialogs = mInputMethodDialogs; final int N = dialogs.size(); - if (DEBUG_INPUT_METHOD) Log.v(TAG, "Removing " + N + " dialogs w/pos=" + pos); + if (DEBUG_INPUT_METHOD) Slog.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); + Slog.v(TAG, "Window list w/pos=" + pos); logWindowList(" "); } @@ -1109,14 +1134,14 @@ public class WindowManagerService extends IWindowManager.Stub pos++; } } - if (DEBUG_INPUT_METHOD) Log.v(TAG, "Adding " + N + " dialogs at pos=" + pos); + if (DEBUG_INPUT_METHOD) Slog.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:"); + Slog.v(TAG, "Final window list:"); logWindowList(" "); } return; @@ -1126,7 +1151,7 @@ public class WindowManagerService extends IWindowManager.Stub win.mTargetAppToken = null; reAddWindowToListInOrderLocked(win); if (DEBUG_INPUT_METHOD) { - Log.v(TAG, "No IM target, final list:"); + Slog.v(TAG, "No IM target, final list:"); logWindowList(" "); } } @@ -1185,18 +1210,18 @@ public class WindowManagerService extends IWindowManager.Stub if (imWin != null) { if (DEBUG_INPUT_METHOD) { - Log.v(TAG, "Moving IM from " + imPos); + Slog.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 + ":"); + Slog.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 + ":"); + Slog.v(TAG, "List after moving IM to " + imPos + ":"); logWindowList(" "); } if (DN > 0) moveInputMethodDialogsLocked(imPos+1); @@ -1209,12 +1234,12 @@ public class WindowManagerService extends IWindowManager.Stub // because they aren't currently associated with a focus window. if (imWin != null) { - if (DEBUG_INPUT_METHOD) Log.v(TAG, "Moving IM from " + imPos); + if (DEBUG_INPUT_METHOD) Slog.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:"); + Slog.v(TAG, "List with no IM target:"); logWindowList(" "); } if (DN > 0) moveInputMethodDialogsLocked(-1);; @@ -1236,7 +1261,7 @@ public class WindowManagerService extends IWindowManager.Stub } final boolean isWallpaperVisible(WindowState wallpaperTarget) { - if (DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper vis: target obscured=" + if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target obscured=" + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??") + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null) ? wallpaperTarget.mAppToken.animation : null) @@ -1248,16 +1273,16 @@ public class WindowManagerService extends IWindowManager.Stub || mUpperWallpaperTarget != null || mLowerWallpaperTarget != null; } - + static final int ADJUST_WALLPAPER_LAYERS_CHANGED = 1<<1; static final int ADJUST_WALLPAPER_VISIBILITY_CHANGED = 1<<2; - + int adjustWallpaperWindowsLocked() { int changed = 0; - + final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); - + // First find top-most window that has asked to be on top of the // wallpaper; all wallpapers go behind it. final ArrayList localmWindows = mWindows; @@ -1283,19 +1308,19 @@ public class WindowManagerService extends IWindowManager.Stub // If this window's app token is hidden and not animating, // it is of no interest to us. if (w.mAppToken.hidden && w.mAppToken.animation == null) { - if (DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden or animating token: " + w); topCurW = null; continue; } } - if (DEBUG_WALLPAPER) Log.v(TAG, "Win " + w + ": readyfordisplay=" + if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": readyfordisplay=" + w.isReadyForDisplay() + " drawpending=" + w.mDrawPending + " commitdrawpending=" + w.mCommitDrawPending); if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isReadyForDisplay() && (mWallpaperTarget == w || (!w.mDrawPending && !w.mCommitDrawPending))) { - if (DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper activity: #" + i + "=" + w); foundW = w; foundI = i; @@ -1305,7 +1330,7 @@ public class WindowManagerService extends IWindowManager.Stub // The current wallpaper target is animating, so we'll // look behind it for another possible target and figure // out what is going on below. - if (DEBUG_WALLPAPER) Log.v(TAG, "Win " + w + if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": token animating, looking behind."); continue; } @@ -1323,29 +1348,29 @@ public class WindowManagerService extends IWindowManager.Stub // enough (we'll just wait until whatever transition is pending // executes). if (mWallpaperTarget != null && mWallpaperTarget.mAppToken != null) { - if (DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper not changing: waiting for app anim in current target"); return 0; } if (foundW != null && foundW.mAppToken != null) { - if (DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper not changing: waiting for app anim in found target"); return 0; } } - + if (mWallpaperTarget != foundW) { if (DEBUG_WALLPAPER) { - Log.v(TAG, "New wallpaper target: " + foundW + Slog.v(TAG, "New wallpaper target: " + foundW + " oldTarget: " + mWallpaperTarget); } - + mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; - + WindowState oldW = mWallpaperTarget; mWallpaperTarget = foundW; - + // Now what is happening... if the current and new targets are // animating, then we are in our super special mode! if (foundW != null && oldW != null) { @@ -1354,36 +1379,36 @@ public class WindowManagerService extends IWindowManager.Stub boolean foundAnim = foundW.mAnimation != null || (foundW.mAppToken != null && foundW.mAppToken.animation != null); if (DEBUG_WALLPAPER) { - Log.v(TAG, "New animation: " + foundAnim + Slog.v(TAG, "New animation: " + foundAnim + " old animation: " + oldAnim); } if (foundAnim && oldAnim) { int oldI = localmWindows.indexOf(oldW); if (DEBUG_WALLPAPER) { - Log.v(TAG, "New i: " + foundI + " old i: " + oldI); + Slog.v(TAG, "New i: " + foundI + " old i: " + oldI); } if (oldI >= 0) { if (DEBUG_WALLPAPER) { - Log.v(TAG, "Animating wallpapers: old#" + oldI + Slog.v(TAG, "Animating wallpapers: old#" + oldI + "=" + oldW + "; new#" + foundI + "=" + foundW); } - + // Set the new target correctly. if (foundW.mAppToken != null && foundW.mAppToken.hiddenRequested) { if (DEBUG_WALLPAPER) { - Log.v(TAG, "Old wallpaper still the target."); + Slog.v(TAG, "Old wallpaper still the target."); } mWallpaperTarget = oldW; } - + // Now set the upper and lower wallpaper targets // correctly, and make sure that we are positioning // the wallpaper below the lower. if (foundI > oldI) { // The new target is on top of the old one. if (DEBUG_WALLPAPER) { - Log.v(TAG, "Found target above old target."); + Slog.v(TAG, "Found target above old target."); } mUpperWallpaperTarget = foundW; mLowerWallpaperTarget = oldW; @@ -1392,7 +1417,7 @@ public class WindowManagerService extends IWindowManager.Stub } else { // The new target is below the old one. if (DEBUG_WALLPAPER) { - Log.v(TAG, "Found target below old target."); + Slog.v(TAG, "Found target below old target."); } mUpperWallpaperTarget = oldW; mLowerWallpaperTarget = foundW; @@ -1400,7 +1425,7 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + } else if (mLowerWallpaperTarget != null) { // Is it time to stop animating? boolean lowerAnimating = mLowerWallpaperTarget.mAnimation != null @@ -1411,31 +1436,31 @@ public class WindowManagerService extends IWindowManager.Stub && mUpperWallpaperTarget.mAppToken.animation != null); if (!lowerAnimating || !upperAnimating) { if (DEBUG_WALLPAPER) { - Log.v(TAG, "No longer animating wallpaper targets!"); + Slog.v(TAG, "No longer animating wallpaper targets!"); } mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; } } - + boolean visible = foundW != null; if (visible) { // The window is visible to the compositor... but is it visible // to the user? That is what the wallpaper cares about. visible = isWallpaperVisible(foundW); - if (DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper visibility: " + visible); - + if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible); + // If the wallpaper target is animating, we may need to copy // its layer adjustment. Only do this if we are not transfering // between two wallpaper targets. mWallpaperAnimLayerAdjustment = (mLowerWallpaperTarget == null && foundW.mAppToken != null) ? foundW.mAppToken.animLayerAdjustment : 0; - + final int maxLayer = mPolicy.getMaxWallpaperLayer() * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; - + // Now w is the window we are supposed to be behind... but we // need to be sure to also be behind any of its attached windows, // AND any starting window associated with it, AND below the @@ -1454,9 +1479,9 @@ public class WindowManagerService extends IWindowManager.Stub foundI--; } } else { - if (DEBUG_WALLPAPER) Log.v(TAG, "No wallpaper target"); + if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target"); } - + if (foundW == null && topCurW != null) { // There is no wallpaper target, so it goes at the bottom. // We will assume it is the same place as last time, if known. @@ -1467,7 +1492,7 @@ public class WindowManagerService extends IWindowManager.Stub // what is below it for later. foundW = foundI > 0 ? (WindowState)localmWindows.get(foundI-1) : null; } - + if (visible) { if (mWallpaperTarget.mWallpaperX >= 0) { mLastWallpaperX = mWallpaperTarget.mWallpaperX; @@ -1478,7 +1503,7 @@ public class WindowManagerService extends IWindowManager.Stub mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; } } - + // Start stepping backwards from here, ensuring that our wallpaper windows // are correctly placed. int curTokenIndex = mWallpaperTokens.size(); @@ -1492,33 +1517,33 @@ public class WindowManagerService extends IWindowManager.Stub // correct size. mLayoutNeeded = true; } - + int curWallpaperIndex = token.windows.size(); while (curWallpaperIndex > 0) { curWallpaperIndex--; WindowState wallpaper = token.windows.get(curWallpaperIndex); - + if (visible) { updateWallpaperOffsetLocked(wallpaper, dw, dh, false); } - + // First, make sure the client has the current visibility // state. if (wallpaper.mWallpaperVisible != visible) { wallpaper.mWallpaperVisible = visible; try { - if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG, "Setting visibility of wallpaper " + wallpaper + ": " + visible); wallpaper.mClient.dispatchAppVisibility(visible); } catch (RemoteException e) { } } - + wallpaper.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment; - if (DEBUG_LAYERS || DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper win " + if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper win " + wallpaper + " anim layer: " + wallpaper.mAnimLayer); - + // First, if this window is at the current index, then all // is well. if (wallpaper == foundW) { @@ -1527,35 +1552,35 @@ public class WindowManagerService extends IWindowManager.Stub ? (WindowState)localmWindows.get(foundI-1) : null; continue; } - + // The window didn't match... the current wallpaper window, // wherever it is, is in the wrong place, so make sure it is // not in the list. int oldIndex = localmWindows.indexOf(wallpaper); if (oldIndex >= 0) { - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Wallpaper removing at " + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at " + oldIndex + ": " + wallpaper); localmWindows.remove(oldIndex); if (oldIndex < foundI) { foundI--; } } - + // Now stick it in. - if (DEBUG_WALLPAPER || DEBUG_WINDOW_MOVEMENT) Log.v(TAG, + if (DEBUG_WALLPAPER || DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + foundI); - + localmWindows.add(foundI, wallpaper); changed |= ADJUST_WALLPAPER_LAYERS_CHANGED; } } - + return changed; } void setWallpaperAnimLayerAdjustmentLocked(int adj) { - if (DEBUG_LAYERS || DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "Setting wallpaper layer adj to " + adj); mWallpaperAnimLayerAdjustment = adj; int curTokenIndex = mWallpaperTokens.size(); @@ -1567,7 +1592,7 @@ public class WindowManagerService extends IWindowManager.Stub curWallpaperIndex--; WindowState wallpaper = token.windows.get(curWallpaperIndex); wallpaper.mAnimLayer = wallpaper.mLayer + adj; - if (DEBUG_LAYERS || DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper win " + if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper win " + wallpaper + " anim layer: " + wallpaper.mAnimLayer); } } @@ -1583,7 +1608,7 @@ public class WindowManagerService extends IWindowManager.Stub int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0; changed = wallpaperWin.mXOffset != offset; if (changed) { - if (DEBUG_WALLPAPER) Log.v(TAG, "Update wallpaper " + if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " x: " + offset); wallpaperWin.mXOffset = offset; } @@ -1592,13 +1617,13 @@ public class WindowManagerService extends IWindowManager.Stub wallpaperWin.mWallpaperXStep = wpxs; rawChanged = true; } - + float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f; float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f; int availh = wallpaperWin.mFrame.bottom-wallpaperWin.mFrame.top-dh; offset = availh > 0 ? -(int)(availh*wpy+.5f) : 0; if (wallpaperWin.mYOffset != offset) { - if (DEBUG_WALLPAPER) Log.v(TAG, "Update wallpaper " + if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " y: " + offset); changed = true; wallpaperWin.mYOffset = offset; @@ -1608,10 +1633,10 @@ public class WindowManagerService extends IWindowManager.Stub wallpaperWin.mWallpaperYStep = wpys; rawChanged = true; } - + if (rawChanged) { try { - if (DEBUG_WALLPAPER) Log.v(TAG, "Report new wp offset " + if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset " + wallpaperWin + " x=" + wallpaperWin.mWallpaperX + " y=" + wallpaperWin.mWallpaperY); if (sync) { @@ -1626,15 +1651,15 @@ public class WindowManagerService extends IWindowManager.Stub if ((mLastWallpaperTimeoutTime+WALLPAPER_TIMEOUT_RECOVERY) < start) { try { - if (DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_WALLPAPER) Slog.v(TAG, "Waiting for offset complete..."); mWindowMap.wait(WALLPAPER_TIMEOUT); } catch (InterruptedException e) { } - if (DEBUG_WALLPAPER) Log.v(TAG, "Offset complete!"); + if (DEBUG_WALLPAPER) Slog.v(TAG, "Offset complete!"); if ((start+WALLPAPER_TIMEOUT) < SystemClock.uptimeMillis()) { - Log.i(TAG, "Timeout waiting for wallpaper to offset: " + Slog.i(TAG, "Timeout waiting for wallpaper to offset: " + wallpaperWin); mLastWallpaperTimeoutTime = start; } @@ -1645,10 +1670,10 @@ public class WindowManagerService extends IWindowManager.Stub } catch (RemoteException e) { } } - + return changed; } - + void wallpaperOffsetsComplete(IBinder window) { synchronized (mWindowMap) { if (mWaitingOnWallpaper != null && @@ -1658,13 +1683,13 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + boolean updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) { final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); - + boolean changed = false; - + WindowState target = mWallpaperTarget; if (target != null) { if (target.mWallpaperX >= 0) { @@ -1678,7 +1703,7 @@ public class WindowManagerService extends IWindowManager.Stub mLastWallpaperY = changingTarget.mWallpaperY; } } - + int curTokenIndex = mWallpaperTokens.size(); while (curTokenIndex > 0) { curTokenIndex--; @@ -1695,15 +1720,15 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + return changed; } - + void updateWallpaperVisibilityLocked() { final boolean visible = isWallpaperVisible(mWallpaperTarget); final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); - + int curTokenIndex = mWallpaperTokens.size(); while (curTokenIndex > 0) { curTokenIndex--; @@ -1714,7 +1739,7 @@ public class WindowManagerService extends IWindowManager.Stub // correct size. mLayoutNeeded = true; } - + int curWallpaperIndex = token.windows.size(); while (curWallpaperIndex > 0) { curWallpaperIndex--; @@ -1722,11 +1747,11 @@ public class WindowManagerService extends IWindowManager.Stub if (visible) { updateWallpaperOffsetLocked(wallpaper, dw, dh, false); } - + if (wallpaper.mWallpaperVisible != visible) { wallpaper.mWallpaperVisible = visible; try { - if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG, "Updating visibility of wallpaper " + wallpaper + ": " + visible); wallpaper.mClient.dispatchAppVisibility(visible); @@ -1736,7 +1761,7 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + void sendPointerToWallpaperLocked(WindowState srcWin, MotionEvent pointer, long eventTime) { int curTokenIndex = mWallpaperTokens.size(); @@ -1769,11 +1794,37 @@ public class WindowManagerService extends IWindowManager.Stub } wallpaper.mClient.dispatchPointer(ev, eventTime, false); } catch (RemoteException e) { - Log.w(TAG, "Failure sending pointer to wallpaper", e); + Slog.w(TAG, "Failure sending pointer to wallpaper", e); } } } } + + void dispatchPointerElsewhereLocked(WindowState srcWin, WindowState relWin, + MotionEvent pointer, long eventTime, boolean skipped) { + if (relWin != null) { + mPolicy.dispatchedPointerEventLw(pointer, relWin.mFrame.left, relWin.mFrame.top); + } else { + mPolicy.dispatchedPointerEventLw(pointer, 0, 0); + } + + // If we sent an initial down to the wallpaper, then continue + // sending events until the final up. + if (mSendingPointersToWallpaper) { + if (skipped) { + Slog.i(TAG, "Sending skipped pointer to wallpaper!"); + } + sendPointerToWallpaperLocked(relWin, pointer, eventTime); + + // If we are on top of the wallpaper, then the wallpaper also + // gets to see this movement. + } else if (srcWin != null + && pointer.getAction() == MotionEvent.ACTION_DOWN + && mWallpaperTarget == srcWin + && srcWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) { + sendPointerToWallpaperLocked(relWin, pointer, eventTime); + } + } public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, @@ -1794,25 +1845,27 @@ public class WindowManagerService extends IWindowManager.Stub if (mDisplay == null) { WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); mDisplay = wm.getDefaultDisplay(); + mInitialDisplayWidth = mDisplay.getWidth(); + mInitialDisplayHeight = mDisplay.getHeight(); mQueue.setDisplay(mDisplay); reportNewConfig = true; } if (mWindowMap.containsKey(client.asBinder())) { - Log.w(TAG, "Window " + client + " is already added"); + Slog.w(TAG, "Window " + client + " is already added"); return WindowManagerImpl.ADD_DUPLICATE_ADD; } if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) { - attachedWindow = windowForClientLocked(null, attrs.token); + attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == null) { - Log.w(TAG, "Attempted to add window with token that is not a window: " + Slog.w(TAG, "Attempted to add window with token that is not a window: " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; } if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { - Log.w(TAG, "Attempted to add window with token that is a sub-window: " + Slog.w(TAG, "Attempted to add window with token that is a sub-window: " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; } @@ -1823,17 +1876,17 @@ public class WindowManagerService extends IWindowManager.Stub if (token == null) { if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { - Log.w(TAG, "Attempted to add application window with unknown token " + Slog.w(TAG, "Attempted to add application window with unknown token " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_INPUT_METHOD) { - Log.w(TAG, "Attempted to add input method window with unknown token " + Slog.w(TAG, "Attempted to add input method window with unknown token " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_WALLPAPER) { - Log.w(TAG, "Attempted to add wallpaper window with unknown token " + Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } @@ -1843,29 +1896,29 @@ public class WindowManagerService extends IWindowManager.Stub && attrs.type <= LAST_APPLICATION_WINDOW) { AppWindowToken atoken = token.appWindowToken; if (atoken == null) { - Log.w(TAG, "Attempted to add window with non-application token " + Slog.w(TAG, "Attempted to add window with non-application token " + token + ". Aborting."); return WindowManagerImpl.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { - Log.w(TAG, "Attempted to add window with exiting application token " + Slog.w(TAG, "Attempted to add window with exiting application token " + token + ". Aborting."); return WindowManagerImpl.ADD_APP_EXITING; } if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "**** NO NEED TO START: " + attrs.getTitle()); return WindowManagerImpl.ADD_STARTING_NOT_NEEDED; } } else if (attrs.type == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { - Log.w(TAG, "Attempted to add input method window with bad token " + Slog.w(TAG, "Attempted to add input method window with bad token " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } } else if (attrs.type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { - Log.w(TAG, "Attempted to add wallpaper window with bad token " + Slog.w(TAG, "Attempted to add wallpaper window with bad token " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } @@ -1876,7 +1929,7 @@ public class WindowManagerService extends IWindowManager.Stub if (win.mDeathRecipient == null) { // Client has apparently died, so there is no reason to // continue. - Log.w(TAG, "Adding window client " + client.asBinder() + Slog.w(TAG, "Adding window client " + client.asBinder() + " that is dead, aborting."); return WindowManagerImpl.ADD_APP_EXITING; } @@ -1961,9 +2014,13 @@ public class WindowManagerService extends IWindowManager.Stub mKeyWaiter.handleNewWindowLocked(mCurrentFocus); } } - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "New client " + client.asBinder() + ": window=" + win); + + if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked()) { + reportNewConfig = true; + } } // sendNewConfiguration() checks caller permissions so we must call it with @@ -1973,14 +2030,6 @@ public class WindowManagerService extends IWindowManager.Stub final long origId = Binder.clearCallingIdentity(); if (reportNewConfig) { sendNewConfiguration(); - } else { - // Update Orientation after adding a window, only if the window needs to be - // displayed right away - if (win.isVisibleOrAdding()) { - if (updateOrientationFromAppTokensUnchecked(null, null) != null) { - sendNewConfiguration(); - } - } } Binder.restoreCallingIdentity(origId); @@ -1989,7 +2038,7 @@ public class WindowManagerService extends IWindowManager.Stub public void removeWindow(Session session, IWindow client) { synchronized(mWindowMap) { - WindowState win = windowForClientLocked(session, client); + WindowState win = windowForClientLocked(session, client, false); if (win == null) { return; } @@ -1999,7 +2048,7 @@ public class WindowManagerService extends IWindowManager.Stub public void removeWindowLocked(Session session, WindowState win) { - if (localLOGV || DEBUG_FOCUS) Log.v( + if (localLOGV || DEBUG_FOCUS) Slog.v( TAG, "Remove " + win + " client=" + Integer.toHexString(System.identityHashCode( win.mClient.asBinder())) @@ -2007,7 +2056,7 @@ public class WindowManagerService extends IWindowManager.Stub final long origId = Binder.clearCallingIdentity(); - if (DEBUG_APP_TRANSITIONS) Log.v( + if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Remove " + win + ": mSurface=" + win.mSurface + " mExiting=" + win.mExiting + " isAnimating=" + win.isAnimating() @@ -2022,7 +2071,7 @@ public class WindowManagerService extends IWindowManager.Stub // to hold off on removing the window until the animation is done. // If the display is frozen, just remove immediately, since the // animation wouldn't be seen. - if (win.mSurface != null && !mDisplayFrozen) { + if (win.mSurface != null && !mDisplayFrozen && mPolicy.isScreenOn()) { // If we are not currently running the exit animation, we // need to see about starting one. if (wasVisible=win.isWinVisibleLw()) { @@ -2038,7 +2087,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (win.mExiting || win.isAnimating()) { // The exit animation is running... wait for it! - //Log.i(TAG, "*** Running exit animation..."); + //Slog.i(TAG, "*** Running exit animation..."); win.mExiting = true; win.mRemoveOnExit = true; mLayoutNeeded = true; @@ -2057,8 +2106,9 @@ public class WindowManagerService extends IWindowManager.Stub // 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)); + != mForcedAppOrientation + && updateOrientationFromAppTokensLocked()) { + mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); } updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); Binder.restoreCallingIdentity(origId); @@ -2079,15 +2129,15 @@ public class WindowManagerService extends IWindowManager.Stub if (false) { RuntimeException e = new RuntimeException("here"); e.fillInStackTrace(); - Log.w(TAG, "Removing window " + win, e); + Slog.w(TAG, "Removing window " + win, e); } - + mPolicy.removeWindowLw(win); win.removeLocked(); mWindowMap.remove(win.mClient.asBinder()); mWindows.remove(win); - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Final remove of window: " + win); + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win); if (mInputMethodWindow == win) { mInputMethodWindow = null; @@ -2101,7 +2151,7 @@ public class WindowManagerService extends IWindowManager.Stub if (atoken != null) { atoken.allAppWindows.remove(win); } - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "**** Removing window " + win + ": count=" + token.windows.size()); if (token.windows.size() == 0) { @@ -2124,7 +2174,7 @@ public class WindowManagerService extends IWindowManager.Stub // If this is the last window except for a starting transition // window, we need to get rid of the starting transition. if (DEBUG_STARTING_WINDOW) { - Log.v(TAG, "Schedule remove starting " + token + Slog.v(TAG, "Schedule remove starting " + token + ": no more real windows"); } Message m = mH.obtainMessage(H.REMOVE_STARTING, atoken); @@ -2138,7 +2188,7 @@ public class WindowManagerService extends IWindowManager.Stub } else if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); } - + if (!mInLayout) { assignLayersLocked(); mLayoutNeeded = true; @@ -2149,21 +2199,30 @@ public class WindowManagerService extends IWindowManager.Stub } } + private static void logSurface(WindowState w, String msg, RuntimeException where) { + String str = " SURFACE " + Integer.toHexString(w.hashCode()) + + ": " + msg + " / " + w.mAttrs.getTitle(); + if (where != null) { + Slog.i(TAG, str, where); + } else { + Slog.i(TAG, str); + } + } + private void setTransparentRegionWindow(Session session, IWindow client, Region region) { long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { - WindowState w = windowForClientLocked(session, client); + WindowState w = windowForClientLocked(session, client, false); if ((w != null) && (w.mSurface != null)) { - if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION"); + if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION"); Surface.openTransaction(); try { - if (SHOW_TRANSACTIONS) Log.i( - TAG, " SURFACE " + w.mSurface - + ": transparentRegionHint=" + region); + if (SHOW_TRANSACTIONS) logSurface(w, + "transparentRegionHint=" + region, null); w.mSurface.setTransparentRegionHint(region); } finally { - if (SHOW_TRANSACTIONS) Log.i(TAG, "<<< CLOSE TRANSACTION"); + if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION"); Surface.closeTransaction(); } } @@ -2179,7 +2238,7 @@ public class WindowManagerService extends IWindowManager.Stub long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { - WindowState w = windowForClientLocked(session, client); + WindowState w = windowForClientLocked(session, client, false); if (w != null) { w.mGivenInsetsPending = false; w.mGivenContentInsets.set(contentInsets); @@ -2197,7 +2256,7 @@ public class WindowManagerService extends IWindowManager.Stub public void getWindowDisplayFrame(Session session, IWindow client, Rect outDisplayFrame) { synchronized(mWindowMap) { - WindowState win = windowForClientLocked(session, client); + WindowState win = windowForClientLocked(session, client, false); if (win == null) { outDisplayFrame.setEmpty(); return; @@ -2218,7 +2277,7 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + void wallpaperCommandComplete(IBinder window, Bundle result) { synchronized (mWindowMap) { if (mWaitingOnWallpaper != null && @@ -2228,7 +2287,7 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + public Bundle sendWindowWallpaperCommandLocked(WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) { if (window == mWallpaperTarget || window == mLowerWallpaperTarget @@ -2251,27 +2310,27 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + if (doWait) { // XXX Need to wait for result. } } - + return null; } - + public int relayoutWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, boolean insetsPending, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, - Surface outSurface) { + Configuration outConfig, Surface outSurface) { boolean displayed = false; boolean inTouchMode; - Configuration newConfig = null; + boolean configChanged; long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { - WindowState win = windowForClientLocked(session, client); + WindowState win = windowForClientLocked(session, client, false); if (win == null) { return 0; } @@ -2289,7 +2348,7 @@ public class WindowManagerService extends IWindowManager.Stub attrChanges = win.mAttrs.copyFrom(attrs); } - if (DEBUG_LAYOUT) Log.v(TAG, "Relayout " + win + ": " + win.mAttrs); + if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": " + win.mAttrs); if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) { win.mAlpha = attrs.alpha; @@ -2319,7 +2378,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean wallpaperMayMove = win.mViewVisibility != viewVisibility && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; - + win.mRelayoutCalled = true; final int oldVisibility = win.mViewVisibility; win.mViewVisibility = viewVisibility; @@ -2337,13 +2396,30 @@ public class WindowManagerService extends IWindowManager.Stub if (oldVisibility == View.GONE) { win.mEnterAnimationPending = true; } - if (displayed && win.mSurface != null && !win.mDrawPending - && !win.mCommitDrawPending && !mDisplayFrozen) { - applyEnterAnimationLocked(win); - } - if (displayed && (win.mAttrs.flags - & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) { - win.mTurnOnScreen = true; + if (displayed) { + if (win.mSurface != null && !win.mDrawPending + && !win.mCommitDrawPending && !mDisplayFrozen + && mPolicy.isScreenOn()) { + applyEnterAnimationLocked(win); + } + if ((win.mAttrs.flags + & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) { + if (DEBUG_VISIBILITY) Slog.v(TAG, + "Relayout window turning screen on: " + win); + win.mTurnOnScreen = true; + } + int diff = 0; + if (win.mConfiguration != mCurConfiguration + && (win.mConfiguration == null + || (diff=mCurConfiguration.diff(win.mConfiguration)) != 0)) { + win.mConfiguration = mCurConfiguration; + if (DEBUG_CONFIGURATION) { + Slog.i(TAG, "Window " + win + " visible with new config: " + + win.mConfiguration + " / 0x" + + Integer.toHexString(diff)); + } + outConfig.setTo(mCurConfiguration); + } } if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) { // To change the format, we need to re-build the surface. @@ -2356,7 +2432,7 @@ public class WindowManagerService extends IWindowManager.Stub outSurface.copyFrom(surface); win.mReportDestroySurface = false; win.mSurfacePendingDestroy = false; - if (SHOW_TRANSACTIONS) Log.i(TAG, + if (SHOW_TRANSACTIONS) Slog.i(TAG, " OUT SURFACE " + outSurface + ": copied"); } else { // For some reason there isn't a surface. Clear the @@ -2364,7 +2440,7 @@ public class WindowManagerService extends IWindowManager.Stub outSurface.release(); } } catch (Exception e) { - Log.w(TAG, "Exception thrown when creating surface for client " + Slog.w(TAG, "Exception thrown when creating surface for client " + client + " (" + win.mAttrs.getTitle() + ")", e); Binder.restoreCallingIdentity(origId); @@ -2386,14 +2462,15 @@ public class WindowManagerService extends IWindowManager.Stub // to provide the correct semantics while starting. final int mask = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; WindowManager.LayoutParams sa = win.mAppToken.startingWindow.mAttrs; sa.flags = (sa.flags&~mask) | (win.mAttrs.flags&mask); } } else { win.mEnterAnimationPending = false; if (win.mSurface != null) { - if (DEBUG_VISIBILITY) Log.i(TAG, "Relayout invis " + win + if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win + ": mExiting=" + win.mExiting + " mSurfacePendingDestroy=" + win.mSurfacePendingDestroy); // If we are not currently running the exit animation, we @@ -2429,7 +2506,7 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + if (win.mSurface == null || (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING) == 0 || win.mSurfacePendingDestroy) { @@ -2439,9 +2516,9 @@ public class WindowManagerService extends IWindowManager.Stub // destroyed at this point. win.mSurfacePendingDestroy = false; outSurface.release(); - if (DEBUG_VISIBILITY) Log.i(TAG, "Releasing surface in: " + win); + if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win); } else if (win.mSurface != null) { - if (DEBUG_VISIBILITY) Log.i(TAG, + if (DEBUG_VISIBILITY) Slog.i(TAG, "Keeping surface, will report destroy: " + win); win.mReportDestroySurface = true; outSurface.copyFrom(win.mSurface); @@ -2481,7 +2558,7 @@ public class WindowManagerService extends IWindowManager.Stub if (assignLayers) { assignLayersLocked(); } - newConfig = updateOrientationFromAppTokensLocked(null, null); + configChanged = updateOrientationFromAppTokensLocked(); performLayoutAndPlaceSurfacesLocked(); if (displayed && win.mIsWallpaper) { updateWallpaperOffsetLocked(win, mDisplay.getWidth(), @@ -2493,7 +2570,7 @@ public class WindowManagerService extends IWindowManager.Stub outFrame.set(win.mFrame); outContentInsets.set(win.mContentInsets); outVisibleInsets.set(win.mVisibleInsets); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Relayout given client " + client.asBinder() + ", requestedWidth=" + requestedWidth + ", requestedHeight=" + requestedHeight @@ -2501,13 +2578,13 @@ public class WindowManagerService extends IWindowManager.Stub + "\nRelayout returning frame=" + outFrame + ", surface=" + outSurface); - if (localLOGV || DEBUG_FOCUS) Log.v( + if (localLOGV || DEBUG_FOCUS) Slog.v( TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange); inTouchMode = mInTouchMode; } - if (newConfig != null) { + if (configChanged) { sendNewConfiguration(); } @@ -2520,7 +2597,7 @@ public class WindowManagerService extends IWindowManager.Stub public void finishDrawingWindow(Session session, IWindow client) { final long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { - WindowState win = windowForClientLocked(session, client); + WindowState win = windowForClientLocked(session, client, false); if (win != null && win.finishDrawingLocked()) { if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); @@ -2533,7 +2610,7 @@ public class WindowManagerService extends IWindowManager.Stub } private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) { - if (DEBUG_ANIM) Log.v(TAG, "Loading animations: params package=" + if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: params package=" + (lp != null ? lp.packageName : null) + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null)); if (lp != null && lp.windowAnimations != 0) { @@ -2545,7 +2622,7 @@ public class WindowManagerService extends IWindowManager.Stub if ((resId&0xFF000000) == 0x01000000) { packageName = "android"; } - if (DEBUG_ANIM) Log.v(TAG, "Loading animations: picked package=" + if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package=" + packageName); return AttributeCache.instance().get(packageName, resId, com.android.internal.R.styleable.WindowAnimation); @@ -2554,13 +2631,13 @@ public class WindowManagerService extends IWindowManager.Stub } private AttributeCache.Entry getCachedAnimations(String packageName, int resId) { - if (DEBUG_ANIM) Log.v(TAG, "Loading animations: params package=" + if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: params package=" + packageName + " resId=0x" + Integer.toHexString(resId)); if (packageName != null) { if ((resId&0xFF000000) == 0x01000000) { packageName = "android"; } - if (DEBUG_ANIM) Log.v(TAG, "Loading animations: picked package=" + if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package=" + packageName); return AttributeCache.instance().get(packageName, resId, com.android.internal.R.styleable.WindowAnimation); @@ -2590,7 +2667,7 @@ public class WindowManagerService extends IWindowManager.Stub // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. - if (!mDisplayFrozen) { + if (!mDisplayFrozen && mPolicy.isScreenOn()) { int anim = mPolicy.selectAnimationLw(win, transit); int attr = -1; Animation a = null; @@ -2615,15 +2692,18 @@ public class WindowManagerService extends IWindowManager.Stub a = loadAnimation(win.mAttrs, attr); } } - if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: win=" + win + if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: win=" + win + " anim=" + anim + " attr=0x" + Integer.toHexString(attr) + " mAnimation=" + win.mAnimation + " isEntrance=" + isEntrance); if (a != null) { if (DEBUG_ANIM) { - RuntimeException e = new RuntimeException(); - if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); - Log.v(TAG, "Loaded animation " + a + " for " + win, e); + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.v(TAG, "Loaded animation " + a + " for " + win, e); } win.setAnimation(a); win.mAnimationIsEntrance = isEntrance; @@ -2673,11 +2753,11 @@ public class WindowManagerService extends IWindowManager.Stub // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. - if (!mDisplayFrozen) { + if (!mDisplayFrozen && mPolicy.isScreenOn()) { Animation a; if (lp != null && (lp.flags & FLAG_COMPATIBLE_WINDOW) != 0) { a = new FadeInOutAnimation(enter); - if (DEBUG_ANIM) Log.v(TAG, + if (DEBUG_ANIM) Slog.v(TAG, "applying FadeInOutAnimation for a window in compatibility mode"); } else if (mNextAppTransitionPackage != null) { a = loadAnimation(mNextAppTransitionPackage, enter ? @@ -2737,16 +2817,19 @@ public class WindowManagerService extends IWindowManager.Stub break; } a = animAttr != 0 ? loadAnimation(lp, animAttr) : null; - if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: wtoken=" + wtoken + if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: wtoken=" + wtoken + " anim=" + a + " animAttr=0x" + Integer.toHexString(animAttr) + " transit=" + transit); } if (a != null) { if (DEBUG_ANIM) { - RuntimeException e = new RuntimeException(); - if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); - Log.v(TAG, "Loaded animation " + a + " for " + wtoken, e); + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.v(TAG, "Loaded animation " + a + " for " + wtoken, e); } wtoken.setAnimation(a); } @@ -2771,20 +2854,20 @@ public class WindowManagerService extends IWindowManager.Stub continue; } if (tokens.get(v) != wtoken.token) { - Log.w(TAG, "Tokens out of sync: external is " + tokens.get(v) + Slog.w(TAG, "Tokens out of sync: external is " + tokens.get(v) + " @ " + v + ", internal is " + wtoken.token + " @ " + m); } v--; m--; } while (v >= 0) { - Log.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v); + Slog.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v); v--; } while (m >= 0) { AppWindowToken wtoken = mAppTokens.get(m); if (!wtoken.removed) { - Log.w(TAG, "Invalid internal token: " + wtoken.token + " @ " + m); + Slog.w(TAG, "Invalid internal token: " + wtoken.token + " @ " + m); } m--; } @@ -2804,7 +2887,7 @@ public class WindowManagerService extends IWindowManager.Stub + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + permission; - Log.w(TAG, msg); + Slog.w(TAG, msg); return false; } @@ -2825,7 +2908,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { WindowToken wtoken = mTokenMap.get(token); if (wtoken != null) { - Log.w(TAG, "Attempted to add existing input method token: " + token); + Slog.w(TAG, "Attempted to add existing input method token: " + token); return; } wtoken = new WindowToken(token, type, true); @@ -2885,7 +2968,7 @@ public class WindowManagerService extends IWindowManager.Stub } } else { - Log.w(TAG, "Attempted to remove non-existing token: " + token); + Slog.w(TAG, "Attempted to remove non-existing token: " + token); } } Binder.restoreCallingIdentity(origId); @@ -2901,7 +2984,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken != null) { - Log.w(TAG, "Attempted to add existing app token: " + token); + Slog.w(TAG, "Attempted to add existing app token: " + token); return; } wtoken = new AppWindowToken(token); @@ -2909,7 +2992,7 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.appFullscreen = fullscreen; wtoken.requestedOrientation = requestedOrientation; mAppTokens.add(addPos, wtoken); - if (localLOGV) Log.v(TAG, "Adding new app token: " + wtoken); + if (localLOGV) Slog.v(TAG, "Adding new app token: " + wtoken); mTokenMap.put(token.asBinder(), wtoken); mTokenList.add(wtoken); @@ -2930,7 +3013,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token); if (wtoken == null) { - Log.w(TAG, "Attempted to set group id of non-existing app token: " + token); + Slog.w(TAG, "Attempted to set group id of non-existing app token: " + token); return; } wtoken.groupId = groupId; @@ -2947,7 +3030,7 @@ public class WindowManagerService extends IWindowManager.Stub // app window. No point in continuing further. return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - if (!wtoken.isVisibleLw()) { + if (!wtoken.isVisibleLw() || !wtoken.mPolicyVisibilityAfterAnim) { continue; } int req = wtoken.mAttrs.screenOrientation; @@ -2962,62 +3045,62 @@ public class WindowManagerService extends IWindowManager.Stub } public int getOrientationFromAppTokensLocked() { - int pos = mAppTokens.size() - 1; - int curGroup = 0; - int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - boolean findingBehind = false; - boolean haveGroup = false; - boolean lastFullscreen = false; - while (pos >= 0) { - AppWindowToken wtoken = mAppTokens.get(pos); - pos--; - // if we're about to tear down this window and not seek for - // the behind activity, don't use it for orientation - if (!findingBehind - && (!wtoken.hidden && wtoken.hiddenRequested)) { + int pos = mAppTokens.size() - 1; + int curGroup = 0; + int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + boolean findingBehind = false; + boolean haveGroup = false; + boolean lastFullscreen = false; + while (pos >= 0) { + AppWindowToken wtoken = mAppTokens.get(pos); + pos--; + // if we're about to tear down this window and not seek for + // the behind activity, don't use it for orientation + if (!findingBehind + && (!wtoken.hidden && wtoken.hiddenRequested)) { + continue; + } + + if (!haveGroup) { + // We ignore any hidden applications on the top. + if (wtoken.hiddenRequested || wtoken.willBeHidden) { continue; } - - if (!haveGroup) { - // We ignore any hidden applications on the top. - if (wtoken.hiddenRequested || wtoken.willBeHidden) { - continue; - } - haveGroup = true; - curGroup = wtoken.groupId; - lastOrientation = wtoken.requestedOrientation; - } 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, and the last app was - // full screen, then we'll stick with the - // user's orientation. - if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND - && lastFullscreen) { - return lastOrientation; - } - } - int or = wtoken.requestedOrientation; - // If this application is fullscreen, and didn't explicitly say - // to use the orientation behind it, then just take whatever - // orientation it has and ignores whatever is under it. - lastFullscreen = wtoken.appFullscreen; - if (lastFullscreen - && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { - return or; - } - // If this application has requested an explicit orientation, - // then use it. - if (or == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || - or == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || - or == ActivityInfo.SCREEN_ORIENTATION_SENSOR || - or == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR || - or == ActivityInfo.SCREEN_ORIENTATION_USER) { - return or; - } - findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND); - } - return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + haveGroup = true; + curGroup = wtoken.groupId; + lastOrientation = wtoken.requestedOrientation; + } 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, and the last app was + // full screen, then we'll stick with the + // user's orientation. + if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND + && lastFullscreen) { + return lastOrientation; + } + } + int or = wtoken.requestedOrientation; + // If this application is fullscreen, and didn't explicitly say + // to use the orientation behind it, then just take whatever + // orientation it has and ignores whatever is under it. + lastFullscreen = wtoken.appFullscreen; + if (lastFullscreen + && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + return or; + } + // If this application has requested an explicit orientation, + // then use it. + if (or == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || + or == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || + or == ActivityInfo.SCREEN_ORIENTATION_SENSOR || + or == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR || + or == ActivityInfo.SCREEN_ORIENTATION_USER) { + return or; + } + findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND); + } + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } public Configuration updateOrientationFromAppTokens( @@ -3026,82 +3109,85 @@ public class WindowManagerService extends IWindowManager.Stub "updateOrientationFromAppTokens()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - - Configuration config; - long ident = Binder.clearCallingIdentity(); - config = updateOrientationFromAppTokensUnchecked(currentConfig, - freezeThisOneIfNeeded); - Binder.restoreCallingIdentity(ident); - return config; - } - Configuration updateOrientationFromAppTokensUnchecked( - Configuration currentConfig, IBinder freezeThisOneIfNeeded) { - Configuration config; + Configuration config = null; + long ident = Binder.clearCallingIdentity(); + synchronized(mWindowMap) { - config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded); - if (config != null) { - mLayoutNeeded = true; - performLayoutAndPlaceSurfacesLocked(); + if (updateOrientationFromAppTokensLocked()) { + if (freezeThisOneIfNeeded != null) { + AppWindowToken wtoken = findAppWindowToken( + freezeThisOneIfNeeded); + if (wtoken != null) { + startAppFreezingScreenLocked(wtoken, + ActivityInfo.CONFIG_ORIENTATION); + } + } + config = computeNewConfigurationLocked(); + + } else if (currentConfig != null) { + // No obvious action we need to take, but if our current + // state mismatches the activity maanager's, update it + mTempConfiguration.setToDefaults(); + if (computeNewConfigurationLocked(mTempConfiguration)) { + if (currentConfig.diff(mTempConfiguration) != 0) { + mWaitingForConfig = true; + mLayoutNeeded = true; + startFreezingDisplayLocked(); + config = new Configuration(mTempConfiguration); + } + } } } + + Binder.restoreCallingIdentity(ident); return config; } /* + * Determine the new desired orientation of the display, returning + * a non-null new Configuration if it has changed from the current + * orientation. IF TRUE IS RETURNED SOMEONE MUST CALL + * setNewConfiguration() TO TELL THE WINDOW MANAGER IT CAN UNFREEZE THE + * SCREEN. This will typically be done for you if you call + * sendNewConfiguration(). + * * 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( - Configuration appConfig, IBinder freezeThisOneIfNeeded) { + boolean updateOrientationFromAppTokensLocked() { + if (mDisplayFrozen) { + // If the display is frozen, some activities may be in the middle + // of restarting, and thus have removed their old window. If the + // window has the flag to hide the lock screen, then the lock screen + // can re-appear and inflict its own orientation on us. Keep the + // orientation stable until this all settles down. + return false; + } + 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.setCurrentOrientationLw(req); - } - - if (changed) { - changed = setRotationUncheckedLocked( - WindowManagerPolicy.USE_LAST_ROTATION, - mLastRotationFlags & (~Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE)); - if (changed) { - if (freezeThisOneIfNeeded != null) { - AppWindowToken wtoken = findAppWindowToken( - freezeThisOneIfNeeded); - if (wtoken != null) { - startAppFreezingScreenLocked(wtoken, - ActivityInfo.CONFIG_ORIENTATION); - } - } - return computeNewConfigurationLocked(); + if (setRotationUncheckedLocked(WindowManagerPolicy.USE_LAST_ROTATION, + mLastRotationFlags | Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE)) { + changed = true; } } - // No obvious action we need to take, but if our current - // state mismatches the activity maanager's, update it - if (appConfig != null) { - mTempConfiguration.setToDefaults(); - if (computeNewConfigurationLocked(mTempConfiguration)) { - if (appConfig.diff(mTempConfiguration) != 0) { - return new Configuration(mTempConfiguration); - } - } - } + return changed; } finally { Binder.restoreCallingIdentity(ident); } - - return null; } int computeForcedAppOrientationLocked() { @@ -3112,6 +3198,19 @@ public class WindowManagerService extends IWindowManager.Stub return req; } + public void setNewConfiguration(Configuration config) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "setNewConfiguration()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized(mWindowMap) { + mCurConfiguration = new Configuration(config); + mWaitingForConfig = false; + performLayoutAndPlaceSurfacesLocked(); + } + } + public void setAppOrientation(IApplicationToken token, int requestedOrientation) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppOrientation()")) { @@ -3121,7 +3220,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken == null) { - Log.w(TAG, "Attempted to set orientation of non-existing app token: " + token); + Slog.w(TAG, "Attempted to set orientation of non-existing app token: " + token); return; } @@ -3149,19 +3248,19 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { boolean changed = false; if (token == null) { - if (DEBUG_FOCUS) Log.v(TAG, "Clearing focused app, was " + mFocusedApp); + if (DEBUG_FOCUS) Slog.v(TAG, "Clearing focused app, was " + mFocusedApp); changed = mFocusedApp != null; mFocusedApp = null; mKeyWaiter.tickle(); } else { AppWindowToken newFocus = findAppWindowToken(token); if (newFocus == null) { - Log.w(TAG, "Attempted to set focus to non-existing app token: " + token); + Slog.w(TAG, "Attempted to set focus to non-existing app token: " + token); return; } changed = mFocusedApp != newFocus; mFocusedApp = newFocus; - if (DEBUG_FOCUS) Log.v(TAG, "Set focused app to: " + mFocusedApp); + if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp); mKeyWaiter.tickle(); } @@ -3180,10 +3279,10 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized(mWindowMap) { - if (DEBUG_APP_TRANSITIONS) Log.v( + if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Prepare app transition: transit=" + transit + " mNextAppTransition=" + mNextAppTransition); - if (!mDisplayFrozen) { + if (!mDisplayFrozen && mPolicy.isScreenOn()) { if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET || mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { mNextAppTransition = transit; @@ -3219,7 +3318,7 @@ public class WindowManagerService extends IWindowManager.Stub mNextAppTransitionExit = exitAnim; } } - + public void executeAppTransition() { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "executeAppTransition()")) { @@ -3230,7 +3329,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_APP_TRANSITIONS) { RuntimeException e = new RuntimeException("here"); e.fillInStackTrace(); - Log.w(TAG, "Execute app transition: mNextAppTransition=" + Slog.w(TAG, "Execute app transition: mNextAppTransition=" + mNextAppTransition, e); } if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { @@ -3251,20 +3350,20 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized(mWindowMap) { - if (DEBUG_STARTING_WINDOW) Log.v( + if (DEBUG_STARTING_WINDOW) Slog.v( TAG, "setAppStartingIcon: token=" + token + " pkg=" + pkg + " transferFrom=" + transferFrom); AppWindowToken wtoken = findAppWindowToken(token); if (wtoken == null) { - Log.w(TAG, "Attempted to set icon of non-existing app token: " + token); + Slog.w(TAG, "Attempted to set icon of non-existing app token: " + token); return; } // If the display is frozen, we won't do anything until the // actual window is displayed so there is no reason to put in // the starting window. - if (mDisplayFrozen) { + if (mDisplayFrozen || !mPolicy.isScreenOn()) { return; } @@ -3283,7 +3382,7 @@ public class WindowManagerService extends IWindowManager.Stub // shown immediately without any more transitions. mSkipAppTransitionAnimation = true; } - if (DEBUG_STARTING_WINDOW) Log.v(TAG, + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Moving existing starting from " + ttoken + " to " + wtoken); final long origId = Binder.clearCallingIdentity(); @@ -3300,7 +3399,7 @@ public class WindowManagerService extends IWindowManager.Stub startingWindow.mToken = wtoken; startingWindow.mRootToken = wtoken; startingWindow.mAppToken = wtoken; - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Removing starting window: " + startingWindow); mWindows.remove(startingWindow); ttoken.windows.remove(startingWindow); @@ -3345,7 +3444,7 @@ public class WindowManagerService extends IWindowManager.Stub } else if (ttoken.startingData != null) { // The previous app was getting ready to show a // starting window, but hasn't yet done so. Steal it! - if (DEBUG_STARTING_WINDOW) Log.v(TAG, + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Moving pending starting from " + ttoken + " to " + wtoken); wtoken.startingData = ttoken.startingData; @@ -3387,7 +3486,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } } - + mStartingIconInTransition = true; wtoken.startingData = new StartingData( pkg, theme, nonLocalizedLabel, @@ -3411,7 +3510,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { wtoken = findAppWindowToken(token); if (wtoken == null) { - Log.w(TAG, "Attempted to set will be hidden of non-existing app token: " + token); + Slog.w(TAG, "Attempted to set will be hidden of non-existing app token: " + token); return; } wtoken.willBeHidden = true; @@ -3431,7 +3530,7 @@ public class WindowManagerService extends IWindowManager.Stub if (wtoken.hidden == visible) { final int N = wtoken.allAppWindows.size(); boolean changed = false; - if (DEBUG_APP_TRANSITIONS) Log.v( + if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden + " performLayout=" + performLayout); @@ -3458,7 +3557,7 @@ public class WindowManagerService extends IWindowManager.Stub delayed = true; } - //Log.i(TAG, "Window " + win + ": vis=" + win.isVisible()); + //Slog.i(TAG, "Window " + win + ": vis=" + win.isVisible()); //win.dump(" "); if (visible) { if (!win.isVisibleNow()) { @@ -3493,7 +3592,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "setTokenVisibilityLocked: " + wtoken + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "setTokenVisibilityLocked: " + wtoken + ": hidden=" + wtoken.hidden + " hiddenRequested=" + wtoken.hiddenRequested); @@ -3524,14 +3623,17 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { wtoken = findAppWindowToken(token); if (wtoken == null) { - Log.w(TAG, "Attempted to set visibility of non-existing app token: " + token); + Slog.w(TAG, "Attempted to set visibility of non-existing app token: " + token); return; } if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) { - RuntimeException e = new RuntimeException(); - if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); - Log.v(TAG, "setAppVisibility(" + token + ", " + visible + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.v(TAG, "setAppVisibility(" + token + ", " + visible + "): mNextAppTransition=" + mNextAppTransition + " hidden=" + wtoken.hidden + " hiddenRequested=" + wtoken.hiddenRequested, e); @@ -3539,14 +3641,15 @@ public class WindowManagerService extends IWindowManager.Stub // If we are preparing an app transition, then delay changing // the visibility of this token until we execute that transition. - if (!mDisplayFrozen && mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { + if (!mDisplayFrozen && mPolicy.isScreenOn() + && mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { // Already in requested state, don't do anything more. if (wtoken.hiddenRequested != visible) { return; } wtoken.hiddenRequested = !visible; - if (DEBUG_APP_TRANSITIONS) Log.v( + if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Setting dummy animation on: " + wtoken); wtoken.setDummyAnimation(); mOpeningApps.remove(wtoken); @@ -3557,14 +3660,14 @@ public class WindowManagerService extends IWindowManager.Stub mOpeningApps.add(wtoken); wtoken.startingDisplayed = false; wtoken.startingMoved = false; - + // If the token is currently hidden (should be the // common case), then we need to set up to wait for // its windows to be ready. if (wtoken.hidden) { wtoken.allDrawn = false; wtoken.waitingToShow = true; - + if (wtoken.clientHidden) { // In the case where we are making an app visible // but holding off for a transition, we still need @@ -3578,7 +3681,7 @@ public class WindowManagerService extends IWindowManager.Stub } } else { mClosingApps.add(wtoken); - + // If the token is currently visible (should be the // common case), then set up to wait for it to be hidden. if (!wtoken.hidden) { @@ -3598,7 +3701,7 @@ public class WindowManagerService extends IWindowManager.Stub void unsetAppFreezingScreenLocked(AppWindowToken wtoken, boolean unfreezeSurfaceNow, boolean force) { if (wtoken.freezingScreen) { - if (DEBUG_ORIENTATION) Log.v(TAG, "Clear freezing of " + wtoken + if (DEBUG_ORIENTATION) Slog.v(TAG, "Clear freezing of " + wtoken + " force=" + force); final int N = wtoken.allAppWindows.size(); boolean unfrozeWindows = false; @@ -3613,7 +3716,7 @@ public class WindowManagerService extends IWindowManager.Stub } } if (force || unfrozeWindows) { - if (DEBUG_ORIENTATION) Log.v(TAG, "No longer freezing: " + wtoken); + if (DEBUG_ORIENTATION) Slog.v(TAG, "No longer freezing: " + wtoken); wtoken.freezingScreen = false; mAppsFreezingScreen--; } @@ -3622,9 +3725,7 @@ public class WindowManagerService extends IWindowManager.Stub mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } - if (mAppsFreezingScreen == 0 && !mWindowsFreezingScreen) { - stopFreezingDisplayLocked(); - } + stopFreezingDisplayLocked(); } } } @@ -3632,9 +3733,12 @@ public class WindowManagerService extends IWindowManager.Stub public void startAppFreezingScreenLocked(AppWindowToken wtoken, int configChanges) { if (DEBUG_ORIENTATION) { - RuntimeException e = new RuntimeException(); - if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); - Log.i(TAG, "Set freezing of " + wtoken.appToken + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.i(TAG, "Set freezing of " + wtoken.appToken + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.freezingScreen, e); } @@ -3664,14 +3768,14 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized(mWindowMap) { - if (configChanges == 0 && !mDisplayFrozen) { - if (DEBUG_ORIENTATION) Log.v(TAG, "Skipping set freeze of " + token); + if (configChanges == 0 && !mDisplayFrozen && mPolicy.isScreenOn()) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping set freeze of " + token); return; } AppWindowToken wtoken = findAppWindowToken(token); if (wtoken == null || wtoken.appToken == null) { - Log.w(TAG, "Attempted to freeze screen with non-existing app token: " + wtoken); + Slog.w(TAG, "Attempted to freeze screen with non-existing app token: " + wtoken); return; } final long origId = Binder.clearCallingIdentity(); @@ -3692,7 +3796,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } final long origId = Binder.clearCallingIdentity(); - if (DEBUG_ORIENTATION) Log.v(TAG, "Clear freezing of " + token + if (DEBUG_ORIENTATION) Slog.v(TAG, "Clear freezing of " + token + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.freezingScreen); unsetAppFreezingScreenLocked(wtoken, true, force); Binder.restoreCallingIdentity(origId); @@ -3714,7 +3818,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowToken basewtoken = mTokenMap.remove(token); mTokenList.remove(basewtoken); if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) { - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "Removing app token: " + wtoken); + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken); delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_UNSET, true); wtoken.inPendingTransaction = false; mOpeningApps.remove(wtoken); @@ -3726,7 +3830,7 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.waitingToHide = true; delayed = true; } - if (DEBUG_APP_TRANSITIONS) Log.v( + if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Removing app " + wtoken + " delayed=" + delayed + " animation=" + wtoken.animation + " animating=" + wtoken.animating); @@ -3751,13 +3855,13 @@ public class WindowManagerService extends IWindowManager.Stub } unsetAppFreezingScreenLocked(wtoken, true, true); if (mFocusedApp == wtoken) { - if (DEBUG_FOCUS) Log.v(TAG, "Removing focused app token:" + wtoken); + if (DEBUG_FOCUS) Slog.v(TAG, "Removing focused app token:" + wtoken); mFocusedApp = null; updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); mKeyWaiter.tickle(); } } else { - Log.w(TAG, "Attempted to remove non-existing app token: " + token); + Slog.w(TAG, "Attempted to remove non-existing app token: " + token); } if (!delayed && wtoken != null) { @@ -3767,7 +3871,7 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); if (startingToken != null) { - if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Schedule remove starting " + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Schedule remove starting " + startingToken + ": app token removed"); Message m = mH.obtainMessage(H.REMOVE_STARTING, startingToken); mH.sendMessage(m); @@ -3778,13 +3882,13 @@ public class WindowManagerService extends IWindowManager.Stub final int NW = token.windows.size(); for (int i=0; i<NW; i++) { WindowState win = token.windows.get(i); - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Tmp removing app window " + win); + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Tmp removing app window " + win); mWindows.remove(win); int j = win.mChildWindows.size(); while (j > 0) { j--; WindowState cwin = (WindowState)win.mChildWindows.get(j); - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Tmp removing child window " + cwin); mWindows.remove(cwin); } @@ -3794,13 +3898,13 @@ public class WindowManagerService extends IWindowManager.Stub void dumpAppTokensLocked() { for (int i=mAppTokens.size()-1; i>=0; i--) { - Log.v(TAG, " #" + i + ": " + mAppTokens.get(i).token); + Slog.v(TAG, " #" + i + ": " + mAppTokens.get(i).token); } } void dumpWindowsLocked() { for (int i=mWindows.size()-1; i>=0; i--) { - Log.v(TAG, " #" + i + ": " + mWindows.get(i)); + Slog.v(TAG, " #" + i + ": " + mWindows.get(i)); } } @@ -3822,10 +3926,10 @@ public class WindowManagerService extends IWindowManager.Stub // Find the first app token below the new position that has // a window displayed. final AppWindowToken wtoken = mAppTokens.get(tokenPos-1); - if (DEBUG_REORDER) Log.v(TAG, "Looking for lower windows @ " + if (DEBUG_REORDER) Slog.v(TAG, "Looking for lower windows @ " + tokenPos + " -- " + wtoken.token); if (wtoken.sendingToBottom) { - if (DEBUG_REORDER) Log.v(TAG, + if (DEBUG_REORDER) Slog.v(TAG, "Skipping token -- currently sending to bottom"); tokenPos--; continue; @@ -3841,7 +3945,7 @@ public class WindowManagerService extends IWindowManager.Stub if (cwin.mSubLayer >= 0) { for (int pos=NW-1; pos>=0; pos--) { if (mWindows.get(pos) == cwin) { - if (DEBUG_REORDER) Log.v(TAG, + if (DEBUG_REORDER) Slog.v(TAG, "Found child win @" + (pos+1)); return pos+1; } @@ -3850,7 +3954,7 @@ public class WindowManagerService extends IWindowManager.Stub } for (int pos=NW-1; pos>=0; pos--) { if (mWindows.get(pos) == win) { - if (DEBUG_REORDER) Log.v(TAG, "Found win @" + (pos+1)); + if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos+1)); return pos+1; } } @@ -3867,19 +3971,19 @@ public class WindowManagerService extends IWindowManager.Stub for (int j=0; j<NCW; j++) { WindowState cwin = (WindowState)win.mChildWindows.get(j); if (!added && cwin.mSubLayer >= 0) { - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Re-adding child window at " + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding child window at " + index + ": " + cwin); mWindows.add(index, win); index++; added = true; } - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Re-adding window at " + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding window at " + index + ": " + cwin); mWindows.add(index, cwin); index++; } if (!added) { - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Re-adding window at " + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding window at " + index + ": " + win); mWindows.add(index, win); index++; @@ -3902,26 +4006,26 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized(mWindowMap) { - if (DEBUG_REORDER) Log.v(TAG, "Initial app tokens:"); + if (DEBUG_REORDER) Slog.v(TAG, "Initial app tokens:"); if (DEBUG_REORDER) dumpAppTokensLocked(); final AppWindowToken wtoken = findAppWindowToken(token); if (wtoken == null || !mAppTokens.remove(wtoken)) { - Log.w(TAG, "Attempting to reorder token that doesn't exist: " + Slog.w(TAG, "Attempting to reorder token that doesn't exist: " + token + " (" + wtoken + ")"); return; } mAppTokens.add(index, wtoken); - if (DEBUG_REORDER) Log.v(TAG, "Moved " + token + " to " + index + ":"); + if (DEBUG_REORDER) Slog.v(TAG, "Moved " + token + " to " + index + ":"); if (DEBUG_REORDER) dumpAppTokensLocked(); final long origId = Binder.clearCallingIdentity(); - if (DEBUG_REORDER) Log.v(TAG, "Removing windows in " + token + ":"); + if (DEBUG_REORDER) Slog.v(TAG, "Removing windows in " + token + ":"); if (DEBUG_REORDER) dumpWindowsLocked(); if (tmpRemoveAppWindowsLocked(wtoken)) { - if (DEBUG_REORDER) Log.v(TAG, "Adding windows back in:"); + if (DEBUG_REORDER) Slog.v(TAG, "Adding windows back in:"); if (DEBUG_REORDER) dumpWindowsLocked(); reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken); - if (DEBUG_REORDER) Log.v(TAG, "Final window list:"); + if (DEBUG_REORDER) Slog.v(TAG, "Final window list:"); if (DEBUG_REORDER) dumpWindowsLocked(); updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); mLayoutNeeded = true; @@ -3940,7 +4044,7 @@ public class WindowManagerService extends IWindowManager.Stub IBinder token = tokens.get(i); final AppWindowToken wtoken = findAppWindowToken(token); if (!mAppTokens.remove(wtoken)) { - Log.w(TAG, "Attempting to reorder token that doesn't exist: " + Slog.w(TAG, "Attempting to reorder token that doesn't exist: " + token + " (" + wtoken + ")"); i--; N--; @@ -4022,7 +4126,7 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) { moveAppWindowsLocked(tokens, mAppTokens.size()); } @@ -4055,7 +4159,7 @@ public class WindowManagerService extends IWindowManager.Stub pos++; } } - + if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) { moveAppWindowsLocked(tokens, 0); } @@ -4140,7 +4244,7 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + static float fixScale(float scale) { if (scale < 0) scale = 0; else if (scale > 20) scale = 20; @@ -4315,23 +4419,24 @@ public class WindowManagerService extends IWindowManager.Stub final int N = mWindows.size(); for (int i=0; i<N; i++) { WindowState w = (WindowState)mWindows.get(i); - if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) { + if (w.isVisibleLw() && !w.mObscured + && (w.mOrientationChanging || !w.isDrawnLw())) { return; } } mDisplayEnabled = true; if (false) { - Log.i(TAG, "ENABLING SCREEN!"); + Slog.i(TAG, "ENABLING SCREEN!"); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); this.dump(null, pw, null); - Log.i(TAG, sw.toString()); + Slog.i(TAG, sw.toString()); } try { IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger"); if (surfaceFlinger != null) { - //Log.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!"); + //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!"); Parcel data = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, @@ -4339,7 +4444,7 @@ public class WindowManagerService extends IWindowManager.Stub data.recycle(); } } catch (RemoteException ex) { - Log.e(TAG, "Boot completed: SurfaceFlinger is dead!"); + Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!"); } } @@ -4368,7 +4473,7 @@ public class WindowManagerService extends IWindowManager.Stub public void setRotationUnchecked(int rotation, boolean alwaysSendConfiguration, int animFlags) { - if(DEBUG_ORIENTATION) Log.v(TAG, + if(DEBUG_ORIENTATION) Slog.v(TAG, "alwaysSendConfiguration set to "+alwaysSendConfiguration); long origId = Binder.clearCallingIdentity(); @@ -4377,20 +4482,21 @@ public class WindowManagerService extends IWindowManager.Stub changed = setRotationUncheckedLocked(rotation, animFlags); } - if (changed) { - sendNewConfiguration(); - synchronized(mWindowMap) { - mLayoutNeeded = true; - performLayoutAndPlaceSurfacesLocked(); - } - } else if (alwaysSendConfiguration) { - //update configuration ignoring orientation change + if (changed || alwaysSendConfiguration) { sendNewConfiguration(); } Binder.restoreCallingIdentity(origId); } + /** + * Apply a new rotation to the screen, respecting the requests of + * applications. Use WindowManagerPolicy.USE_LAST_ROTATION to simply + * re-evaluate the desired rotation. + * + * Returns null if the rotation has been changed. In this case YOU + * MUST CALL setNewConfiguration() TO UNFREEZE THE SCREEN. + */ public boolean setRotationUncheckedLocked(int rotation, int animFlags) { boolean changed; if (rotation == WindowManagerPolicy.USE_LAST_ROTATION) { @@ -4399,14 +4505,14 @@ public class WindowManagerService extends IWindowManager.Stub mRequestedRotation = rotation; mLastRotationFlags = animFlags; } - if (DEBUG_ORIENTATION) Log.v(TAG, "Overwriting rotation value from " + rotation); + if (DEBUG_ORIENTATION) Slog.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); + if (DEBUG_ORIENTATION) Slog.v(TAG, "new rotation is set to " + rotation); changed = mDisplayEnabled && mRotation != rotation; if (changed) { - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Slog.v(TAG, "Rotation changed to " + rotation + " from " + mRotation + " (forceApp=" + mForcedAppOrientation @@ -4416,8 +4522,10 @@ public class WindowManagerService extends IWindowManager.Stub mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT), 2000); + mWaitingForConfig = true; + mLayoutNeeded = true; startFreezingDisplayLocked(); - Log.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags); + Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags); mQueue.setOrientation(rotation); if (mDisplayEnabled) { Surface.setOrientation(0, rotation, animFlags); @@ -4501,7 +4609,7 @@ public class WindowManagerService extends IWindowManager.Stub try { return mViewServer.start(); } catch (IOException e) { - Log.w(TAG, "View server did not start"); + Slog.w(TAG, "View server did not start"); } } return false; @@ -4511,7 +4619,7 @@ public class WindowManagerService extends IWindowManager.Stub mViewServer = new ViewServer(this, port); return mViewServer.start(); } catch (IOException e) { - Log.w(TAG, "View server did not start"); + Slog.w(TAG, "View server did not start"); } return false; } @@ -4656,7 +4764,7 @@ public class WindowManagerService extends IWindowManager.Stub index = parameters.length(); } final String code = parameters.substring(0, index); - int hashCode = "ffffffff".equals(code) ? -1 : Integer.parseInt(code, 16); + int hashCode = (int) Long.parseLong(code, 16); // Extract the command's parameter after the window description if (index < parameters.length()) { @@ -4686,7 +4794,7 @@ public class WindowManagerService extends IWindowManager.Stub reply.readException(); } catch (Exception e) { - Log.w(TAG, "Could not send command " + command + " with parameters " + parameters, e); + Slog.w(TAG, "Could not send command " + command + " with parameters " + parameters, e); success = false; } finally { if (data != null) { @@ -4750,8 +4858,13 @@ public class WindowManagerService extends IWindowManager.Stub return false; } mQueue.getInputConfiguration(config); - final int dw = mDisplay.getWidth(); - final int dh = mDisplay.getHeight(); + + // Use the effective "visual" dimensions based on current rotation + final boolean rotated = (mRotation == Surface.ROTATION_90 + || mRotation == Surface.ROTATION_270); + final int dw = rotated ? mInitialDisplayHeight : mInitialDisplayWidth; + final int dh = rotated ? mInitialDisplayWidth : mInitialDisplayHeight; + int orientation = Configuration.ORIENTATION_SQUARE; if (dw < dh) { orientation = Configuration.ORIENTATION_PORTRAIT; @@ -4759,7 +4872,7 @@ public class WindowManagerService extends IWindowManager.Stub orientation = Configuration.ORIENTATION_LANDSCAPE; } config.orientation = orientation; - + DisplayMetrics dm = new DisplayMetrics(); mDisplay.getMetrics(dm); CompatibilityInfo.updateCompatibleScreenFrame(dm, orientation, mCompatibleScreenFrame); @@ -4794,7 +4907,7 @@ public class WindowManagerService extends IWindowManager.Stub mScreenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE; } else { mScreenLayout = Configuration.SCREENLAYOUT_SIZE_NORMAL; - + // If this screen is wider than normal HVGA, or taller // than FWVGA, then for old apps we want to run in size // compatibility mode. @@ -4802,7 +4915,7 @@ public class WindowManagerService extends IWindowManager.Stub mScreenLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED; } } - + // Is this a long screen? if (((longSize*3)/5) >= (shortSize-1)) { // Anything wider than WVGA (5:3) is considering to be long. @@ -4813,7 +4926,7 @@ public class WindowManagerService extends IWindowManager.Stub } } config.screenLayout = mScreenLayout; - + config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO; config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; mPolicy.adjustConfigurationLw(config); @@ -4884,7 +4997,7 @@ public class WindowManagerService extends IWindowManager.Stub * @return Returns true if event was dispatched, false if it was dropped for any reason */ private int dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) { - if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Log.v(TAG, + if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Slog.v(TAG, "dispatchPointer " + ev); if (MEASURE_LATENCY) { @@ -4893,7 +5006,7 @@ public class WindowManagerService extends IWindowManager.Stub Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev, ev, true, false, pid, uid); - + if (MEASURE_LATENCY) { lt.sample("3 Last dispatch finished ", System.nanoTime() - qev.whenNano); } @@ -4915,13 +5028,10 @@ public class WindowManagerService extends IWindowManager.Stub // pointer without actually pressing down. All other cases should // be atypical, so let's log them. if (action != MotionEvent.ACTION_MOVE) { - Log.w(TAG, "No window to dispatch pointer action " + ev.getAction()); + Slog.w(TAG, "No window to dispatch pointer action " + ev.getAction()); } synchronized (mWindowMap) { - if (mSendingPointersToWallpaper) { - Log.i(TAG, "Sending skipped pointer to wallpaper!"); - sendPointerToWallpaperLocked(null, ev, ev.getEventTime()); - } + dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true); } if (qev != null) { mQueue.recycleEvent(qev); @@ -4931,10 +5041,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { synchronized (mWindowMap) { - if (mSendingPointersToWallpaper) { - Log.i(TAG, "Sending skipped pointer to wallpaper!"); - sendPointerToWallpaperLocked(null, ev, ev.getEventTime()); - } + dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true); } if (qev != null) { mQueue.recycleEvent(qev); @@ -4947,14 +5054,14 @@ public class WindowManagerService extends IWindowManager.Stub final long eventTime = ev.getEventTime(); final long eventTimeNano = ev.getEventTimeNano(); - - //Log.i(TAG, "Sending " + ev + " to " + target); + + //Slog.i(TAG, "Sending " + ev + " to " + target); if (uid != 0 && uid != target.mSession.mUid) { if (mContext.checkPermission( android.Manifest.permission.INJECT_EVENTS, pid, uid) != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Permission denied: injecting pointer event from pid " + Slog.w(TAG, "Permission denied: injecting pointer event from pid " + pid + " uid " + uid + " to window " + target + " owned by uid " + target.mSession.mUid); if (qev != null) { @@ -4964,7 +5071,7 @@ public class WindowManagerService extends IWindowManager.Stub return INJECT_NO_PERMISSION; } } - + if (MEASURE_LATENCY) { lt.sample("4 in dispatchPointer ", System.nanoTime() - eventTimeNano); } @@ -5003,7 +5110,7 @@ public class WindowManagerService extends IWindowManager.Stub //an invalid move have to cancel earlier action ev.setAction(MotionEvent.ACTION_CANCEL); action = MotionEvent.ACTION_CANCEL; - if (DEBUG_INPUT) Log.v(TAG, "Sending cancel for invalid ACTION_MOVE"); + if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE"); //note that the subsequent invalid moves will not get here mFatTouch = true; } @@ -5059,16 +5166,14 @@ public class WindowManagerService extends IWindowManager.Stub if (!target.isVisibleLw()) { // During this motion dispatch, the target window has become // invisible. - if (mSendingPointersToWallpaper) { - sendPointerToWallpaperLocked(null, ev, eventTime); - } + dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), false); if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); return INJECT_SUCCEEDED; } - + if (qev != null && action == MotionEvent.ACTION_MOVE) { mKeyWaiter.bindTargetWindowLocked(target, KeyWaiter.RETURN_PENDING_POINTER, qev); @@ -5085,7 +5190,7 @@ public class WindowManagerService extends IWindowManager.Stub try { out.mClient.dispatchPointer(oev, eventTime, false); } catch (android.os.RemoteException e) { - Log.i(TAG, "WINDOW DIED during outside motion dispatch: " + out); + Slog.i(TAG, "WINDOW DIED during outside motion dispatch: " + out); } oev.offsetLocation((float)frame.left, (float)frame.top); out = out.mNextOutsideTouch; @@ -5093,15 +5198,9 @@ public class WindowManagerService extends IWindowManager.Stub mKeyWaiter.mOutsideTouchTargets = null; } } - - // If we are on top of the wallpaper, then the wallpaper also - // gets to see this movement. - if ((mWallpaperTarget == target && - target.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) - || mSendingPointersToWallpaper) { - sendPointerToWallpaperLocked(null, ev, eventTime); - } - + + dispatchPointerElsewhereLocked(target, null, ev, ev.getEventTime(), false); + final Rect frame = target.mFrame; ev.offsetLocation(-(float)frame.left, -(float)frame.top); mKeyWaiter.bindTargetWindowLocked(target); @@ -5112,9 +5211,9 @@ public class WindowManagerService extends IWindowManager.Stub // dispatch the event. try { if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) { - Log.v(TAG, "Delivering pointer " + qev + " to " + target); + Slog.v(TAG, "Delivering pointer " + qev + " to " + target); } - + if (MEASURE_LATENCY) { lt.sample("6 before svr->client ipc ", System.nanoTime() - eventTimeNano); } @@ -5126,7 +5225,7 @@ public class WindowManagerService extends IWindowManager.Stub } return INJECT_SUCCEEDED; } catch (android.os.RemoteException e) { - Log.i(TAG, "WINDOW DIED during motion dispatch: " + target); + Slog.i(TAG, "WINDOW DIED during motion dispatch: " + target); mKeyWaiter.mMotionTarget = null; try { removeWindow(target.mSession, target.mClient); @@ -5142,13 +5241,13 @@ public class WindowManagerService extends IWindowManager.Stub * @return Returns true if event was dispatched, false if it was dropped for any reason */ private int dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) { - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">"); Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev, ev, false, false, pid, uid); if (focusObj == null) { - Log.w(TAG, "No focus window, dropping trackball: " + ev); + Slog.w(TAG, "No focus window, dropping trackball: " + ev); if (qev != null) { mQueue.recycleEvent(qev); } @@ -5169,7 +5268,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mContext.checkPermission( android.Manifest.permission.INJECT_EVENTS, pid, uid) != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Permission denied: injecting key event from pid " + Slog.w(TAG, "Permission denied: injecting key event from pid " + pid + " uid " + uid + " to window " + focus + " owned by uid " + focus.mSession.mUid); if (qev != null) { @@ -5198,7 +5297,7 @@ public class WindowManagerService extends IWindowManager.Stub focus.mClient.dispatchTrackball(ev, eventTime, true); return INJECT_SUCCEEDED; } catch (android.os.RemoteException e) { - Log.i(TAG, "WINDOW DIED during key dispatch: " + focus); + Slog.i(TAG, "WINDOW DIED during key dispatch: " + focus); try { removeWindow(focus.mSession, focus.mClient); } catch (java.util.NoSuchElementException ex) { @@ -5214,12 +5313,12 @@ public class WindowManagerService extends IWindowManager.Stub * @return Returns true if event was dispatched, false if it was dropped for any reason */ private int dispatchKey(KeyEvent event, int pid, int uid) { - if (DEBUG_INPUT) Log.v(TAG, "Dispatch key: " + event); + if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event); Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null, null, false, false, pid, uid); if (focusObj == null) { - Log.w(TAG, "No focus window, dropping: " + event); + Slog.w(TAG, "No focus window, dropping: " + event); return INJECT_FAILED; } if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { @@ -5235,17 +5334,17 @@ public class WindowManagerService extends IWindowManager.Stub if (event.getRepeatCount() > 0 && mQueue.hasKeyUpEvent(event)) { return INJECT_SUCCEEDED; } - + WindowState focus = (WindowState)focusObj; - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "Dispatching to " + focus + ": " + event); if (uid != 0 && uid != focus.mSession.mUid) { if (mContext.checkPermission( android.Manifest.permission.INJECT_EVENTS, pid, uid) != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Permission denied: injecting key event from pid " + Slog.w(TAG, "Permission denied: injecting key event from pid " + pid + " uid " + uid + " to window " + focus + " owned by uid " + focus.mSession.mUid); return INJECT_NO_PERMISSION; @@ -5262,13 +5361,13 @@ public class WindowManagerService extends IWindowManager.Stub try { if (DEBUG_INPUT || DEBUG_FOCUS) { - Log.v(TAG, "Delivering key " + event.getKeyCode() + Slog.v(TAG, "Delivering key " + event.getKeyCode() + " to " + focus); } focus.mClient.dispatchKey(event); return INJECT_SUCCEEDED; } catch (android.os.RemoteException e) { - Log.i(TAG, "WINDOW DIED during key dispatch: " + focus); + Slog.i(TAG, "WINDOW DIED during key dispatch: " + focus); try { removeWindow(focus.mSession, focus.mClient); } catch (java.util.NoSuchElementException ex) { @@ -5465,7 +5564,6 @@ public class WindowManagerService extends IWindowManager.Stub 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; @@ -5478,7 +5576,7 @@ public class WindowManagerService extends IWindowManager.Stub + " fin=" + finished + " gfw=" + gotFirstWindow + " ed=" + eventDispatching + " tts=" + timeToSwitch + " wf=" + wasFrozen + " fp=" + focusPaused - + " mcf=" + mCurrentFocus + "}}"; + + " mcf=" + curFocus + "}}"; } }; private DispatchState mDispatchState = null; @@ -5529,7 +5627,7 @@ public class WindowManagerService extends IWindowManager.Stub // it may change before we lock. Thus we must check it again. WindowState targetWin = mLastWin; boolean targetIsNew = targetWin == null; - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "waitForLastKey: mFinished=" + mFinished + ", mLastWin=" + mLastWin); if (targetIsNew) { @@ -5538,12 +5636,12 @@ public class WindowManagerService extends IWindowManager.Stub if (target == SKIP_TARGET_TOKEN) { // The user has pressed a special key, and we are // dropping all pending events before it. - if (DEBUG_INPUT) Log.v(TAG, "Skipping: " + nextKey + if (DEBUG_INPUT) Slog.v(TAG, "Skipping: " + nextKey + " " + nextMotion); return null; } if (target == CONSUMED_EVENT_TOKEN) { - if (DEBUG_INPUT) Log.v(TAG, "Consumed: " + nextKey + if (DEBUG_INPUT) Slog.v(TAG, "Consumed: " + nextKey + " " + nextMotion); return target; } @@ -5567,7 +5665,7 @@ public class WindowManagerService extends IWindowManager.Stub // If event dispatching is disabled, then we // just consume the events. if (!mEventDispatching) { - if (DEBUG_INPUT) Log.v(TAG, + if (DEBUG_INPUT) Slog.v(TAG, "Skipping event; dispatching disabled: " + nextKey + " " + nextMotion); return null; @@ -5583,14 +5681,14 @@ public class WindowManagerService extends IWindowManager.Stub // If we didn't find a target window, and there is no // focused app window, then just eat the events. } else if (mFocusedApp == null) { - if (DEBUG_INPUT) Log.v(TAG, + if (DEBUG_INPUT) Slog.v(TAG, "Skipping event; no focused app: " + nextKey + " " + nextMotion); return null; } } - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "Waiting for last key in " + mLastBinder + " target=" + targetWin + " mFinished=" + mFinished @@ -5611,7 +5709,7 @@ public class WindowManagerService extends IWindowManager.Stub // If an app switch key has been pressed, and we have // waited too long for the current app to finish // processing keys, then wait no more! - doFinishedKeyLocked(true); + doFinishedKeyLocked(false); continue; } long switchTimeout = mTimeToSwitch - now; @@ -5623,10 +5721,10 @@ public class WindowManagerService extends IWindowManager.Stub try { // after that continue // processing keys, so we don't get stuck. - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "Waiting for key dispatch: " + curTimeout); wait(curTimeout); - if (DEBUG_INPUT) Log.v(TAG, "Finished waiting @" + if (DEBUG_INPUT) Slog.v(TAG, "Finished waiting @" + SystemClock.uptimeMillis() + " startTime=" + startTime + " switchTime=" + mTimeToSwitch + " target=" + targetWin + " mLW=" + mLastWin @@ -5649,12 +5747,13 @@ public class WindowManagerService extends IWindowManager.Stub if (waitedFor >= keyDispatchingTimeout && mTimeToSwitch == 0) { IApplicationToken at = null; synchronized (this) { - Log.w(TAG, "Key dispatching timed out sending to " + + Slog.w(TAG, "Key dispatching timed out sending to " + (targetWin != null ? targetWin.mAttrs.getTitle() - : "<null>")); + : "<null>: no window ready for key dispatch")); // NOSHIP debugging - Log.w(TAG, "Dispatch state: " + mDispatchState); - Log.w(TAG, "Current state: " + new DispatchState(nextKey, targetWin)); + Slog.w(TAG, "Previous dispatch state: " + mDispatchState); + Slog.w(TAG, "Current dispatch state: " + + new DispatchState(nextKey, targetWin)); // END NOSHIP //dump(); if (targetWin != null) { @@ -5684,11 +5783,11 @@ public class WindowManagerService extends IWindowManager.Stub if (abort && (mLastWin == targetWin || targetWin == null)) { mFinished = true; if (mLastWin != null) { - if (DEBUG_INPUT) Log.v(TAG, + if (DEBUG_INPUT) Slog.v(TAG, "Window " + mLastWin + " timed out on key input"); if (mLastWin.mToken.paused) { - Log.w(TAG, "Un-pausing dispatching to this window"); + Slog.w(TAG, "Un-pausing dispatching to this window"); mLastWin.mToken.paused = false; } } @@ -5701,7 +5800,7 @@ public class WindowManagerService extends IWindowManager.Stub return null; } } else { - Log.w(TAG, "Continuing to wait for key to be dispatched"); + Slog.w(TAG, "Continuing to wait for key to be dispatched"); startTime = SystemClock.uptimeMillis(); } } @@ -5720,7 +5819,7 @@ public class WindowManagerService extends IWindowManager.Stub final int repeatCount = nextKey.getRepeatCount(); final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP; boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode); - + if (!dispatch) { if (callingUid == 0 || mContext.checkPermission( @@ -5731,7 +5830,7 @@ public class WindowManagerService extends IWindowManager.Stub nextKey.getMetaState(), down, repeatCount, nextKey.getFlags()); } - Log.w(TAG, "Event timeout during app switch: dropping " + Slog.w(TAG, "Event timeout during app switch: dropping " + nextKey); return SKIP_TARGET_TOKEN; } @@ -5752,7 +5851,7 @@ public class WindowManagerService extends IWindowManager.Stub callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) { if (mPolicy.interceptKeyTi(focus, - keycode, nextKey.getMetaState(), down, repeatCount, + keycode, nextKey.getMetaState(), down, repeatCount, nextKey.getFlags())) { return CONSUMED_EVENT_TOKEN; } @@ -5763,7 +5862,7 @@ public class WindowManagerService extends IWindowManager.Stub } else if (!isPointerEvent) { boolean dispatch = mKeyWaiter.checkShouldDispatchKey(-1); if (!dispatch) { - Log.w(TAG, "Event timeout during app switch: dropping trackball " + Slog.w(TAG, "Event timeout during app switch: dropping trackball " + nextMotion); return SKIP_TARGET_TOKEN; } @@ -5784,7 +5883,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean dispatch = mKeyWaiter.checkShouldDispatchKey( KeyEvent.KEYCODE_UNKNOWN); if (!dispatch) { - Log.w(TAG, "Event timeout during app switch: dropping pointer " + Slog.w(TAG, "Event timeout during app switch: dropping pointer " + nextMotion); return SKIP_TARGET_TOKEN; } @@ -5808,7 +5907,7 @@ public class WindowManagerService extends IWindowManager.Stub // already down! // XXX: We should probably send an ACTION_UP to the current // target. - Log.w(TAG, "Pointer down received while already down in: " + Slog.w(TAG, "Pointer down received while already down in: " + mMotionTarget); mMotionTarget = null; } @@ -5824,7 +5923,7 @@ public class WindowManagerService extends IWindowManager.Stub final Rect tmpRect = mTempRect; for (int i=N-1; i>=0; i--) { WindowState child = (WindowState)windows.get(i); - //Log.i(TAG, "Checking dispatch to: " + child); + //Slog.i(TAG, "Checking dispatch to: " + child); final int flags = child.mAttrs.flags; if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) { if (topErrWindow == null) { @@ -5832,11 +5931,11 @@ public class WindowManagerService extends IWindowManager.Stub } } if (!child.isVisibleLw()) { - //Log.i(TAG, "Not visible!"); + //Slog.i(TAG, "Not visible!"); continue; } if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { - //Log.i(TAG, "Not touchable!"); + //Slog.i(TAG, "Not touchable!"); if ((flags & WindowManager.LayoutParams .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { child.mNextOutsideTouch = mOutsideTouchTargets; @@ -5868,12 +5967,12 @@ public class WindowManagerService extends IWindowManager.Stub (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); if (tmpRect.contains(x, y) || touchFlags == 0) { - //Log.i(TAG, "Using this target!"); + //Slog.i(TAG, "Using this target!"); if (!screenWasOff || (flags & WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) { mMotionTarget = child; } else { - //Log.i(TAG, "Waking, skip!"); + //Slog.i(TAG, "Waking, skip!"); mMotionTarget = null; } break; @@ -5883,7 +5982,7 @@ public class WindowManagerService extends IWindowManager.Stub .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { child.mNextOutsideTouch = mOutsideTouchTargets; mOutsideTouchTargets = child; - //Log.i(TAG, "Adding to outside target list: " + child); + //Slog.i(TAG, "Adding to outside target list: " + child); } } @@ -5945,7 +6044,7 @@ public class WindowManagerService extends IWindowManager.Stub releasePendingPointerLocked(s); s.mPendingPointerMove = pendingMotion; s.mPendingPointerWindow = win; - if (DEBUG_INPUT) Log.v(TAG, + if (DEBUG_INPUT) Slog.v(TAG, "bindTargetToWindow " + s.mPendingPointerMove); } else if (pendingWhat == RETURN_PENDING_TRACKBALL) { releasePendingTrackballLocked(s); @@ -5956,7 +6055,7 @@ public class WindowManagerService extends IWindowManager.Stub } void releasePendingPointerLocked(Session s) { - if (DEBUG_INPUT) Log.v(TAG, + if (DEBUG_INPUT) Slog.v(TAG, "releasePendingPointer " + s.mPendingPointerMove); if (s.mPendingPointerMove != null) { mQueue.recycleEvent(s.mPendingPointerMove); @@ -5973,7 +6072,7 @@ public class WindowManagerService extends IWindowManager.Stub MotionEvent finishedKey(Session session, IWindow client, boolean force, int returnWhat) { - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "finishedKey: client=" + client + ", force=" + force); if (client == null) { @@ -5983,9 +6082,9 @@ public class WindowManagerService extends IWindowManager.Stub MotionEvent res = null; QueuedEvent qev = null; WindowState win = null; - + synchronized (this) { - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "finishedKey: client=" + client.asBinder() + ", force=" + force + ", last=" + mLastBinder + " (token=" + (mLastWin != null ? mLastWin.mToken : null) + ")"); @@ -6003,12 +6102,12 @@ public class WindowManagerService extends IWindowManager.Stub } if (mLastBinder == client.asBinder()) { - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "finishedKey: last paused=" + ((mLastWin != null) ? mLastWin.mToken.paused : "null")); if (mLastWin != null && (!mLastWin.mToken.paused || force || !mEventDispatching)) { - doFinishedKeyLocked(false); + doFinishedKeyLocked(true); } else { // Make sure to wake up anyone currently waiting to // dispatch a key, so they can re-evaluate their @@ -6020,7 +6119,7 @@ public class WindowManagerService extends IWindowManager.Stub if (qev != null) { res = (MotionEvent)qev.event; - if (DEBUG_INPUT) Log.v(TAG, + if (DEBUG_INPUT) Slog.v(TAG, "Returning pending motion: " + res); mQueue.recycleEvent(qev); if (win != null && returnWhat == RETURN_PENDING_POINTER) { @@ -6031,11 +6130,7 @@ public class WindowManagerService extends IWindowManager.Stub if (res != null && returnWhat == RETURN_PENDING_POINTER) { synchronized (mWindowMap) { - if ((mWallpaperTarget == win && - win.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) - || mSendingPointersToWallpaper) { - sendPointerToWallpaperLocked(win, res, res.getEventTime()); - } + dispatchPointerElsewhereLocked(win, win, res, res.getEventTime(), false); } } @@ -6053,7 +6148,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } synchronized (this) { - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "New key dispatch window: win=" + newWindow.mClient.asBinder() + ", last=" + mLastBinder @@ -6069,7 +6164,7 @@ public class WindowManagerService extends IWindowManager.Stub mGotFirstWindow = true; if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) { - if (DEBUG_INPUT) Log.v(TAG, + if (DEBUG_INPUT) Slog.v(TAG, "New SYSTEM_ERROR window; resetting state"); mLastWin = null; mLastBinder = null; @@ -6079,20 +6174,16 @@ public class WindowManagerService extends IWindowManager.Stub // If the new window is above the window we are // waiting on, then stop waiting and let key dispatching // start on the new guy. - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "Last win layer=" + mLastWin.mLayer + ", new win layer=" + newWindow.mLayer); if (newWindow.mLayer >= mLastWin.mLayer) { // The new window is above the old; finish pending input to the last // window and start directing it to the new one. mLastWin.mToken.paused = false; - doFinishedKeyLocked(true); // does a notifyAll() + doFinishedKeyLocked(false); // does a notifyAll() + return; } - // 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; } // Now that we've put a new window state in place, make the event waiter @@ -6104,7 +6195,7 @@ public class WindowManagerService extends IWindowManager.Stub void pauseDispatchingLocked(WindowToken token) { synchronized (this) { - if (DEBUG_INPUT) Log.v(TAG, "Pausing WindowToken " + token); + if (DEBUG_INPUT) Slog.v(TAG, "Pausing WindowToken " + token); token.paused = true; /* @@ -6112,11 +6203,11 @@ public class WindowManagerService extends IWindowManager.Stub mPaused = true; } else { if (mLastWin == null) { - Log.i(TAG, "Key dispatching not paused: no last window."); + Slog.i(TAG, "Key dispatching not paused: no last window."); } else if (mFinished) { - Log.i(TAG, "Key dispatching not paused: finished last key."); + Slog.i(TAG, "Key dispatching not paused: finished last key."); } else { - Log.i(TAG, "Key dispatching not paused: window in higher layer."); + Slog.i(TAG, "Key dispatching not paused: window in higher layer."); } } */ @@ -6126,7 +6217,7 @@ public class WindowManagerService extends IWindowManager.Stub void resumeDispatchingLocked(WindowToken token) { synchronized (this) { if (token.paused) { - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "Resuming WindowToken " + token + ", last=" + mLastBinder + " (token=" + (mLastWin != null ? mLastWin.mToken : null) @@ -6134,7 +6225,7 @@ public class WindowManagerService extends IWindowManager.Stub + token.paused); token.paused = false; if (mLastWin != null && mLastWin.mToken == token && mFinished) { - doFinishedKeyLocked(true); + doFinishedKeyLocked(false); } else { notifyAll(); } @@ -6154,7 +6245,7 @@ public class WindowManagerService extends IWindowManager.Stub // Don't wait for more than .5 seconds for app to finish // processing the pending events. long now = SystemClock.uptimeMillis() + 500; - if (DEBUG_INPUT) Log.v(TAG, "appSwitchComing: " + now); + if (DEBUG_INPUT) Slog.v(TAG, "appSwitchComing: " + now); if (mTimeToSwitch == 0 || now < mTimeToSwitch) { mTimeToSwitch = now; } @@ -6162,14 +6253,14 @@ public class WindowManagerService extends IWindowManager.Stub } } - private final void doFinishedKeyLocked(boolean doRecycle) { + private final void doFinishedKeyLocked(boolean force) { if (mLastWin != null) { releasePendingPointerLocked(mLastWin.mSession); releasePendingTrackballLocked(mLastWin.mSession); } - if (mLastWin == null || !mLastWin.mToken.paused - || !mLastWin.isVisibleLw()) { + if (force || mLastWin == null || !mLastWin.mToken.paused + || !mLastWin.isVisibleLw()) { // If the current window has been paused, we aren't -really- // finished... so let the waiters still wait. mLastWin = null; @@ -6254,7 +6345,7 @@ public class WindowManagerService extends IWindowManager.Stub if (screenIsOff) { if (!mPolicy.isWakeRelMovementTq(event.deviceId, device.classes, event)) { - //Log.i(TAG, "dropping because screenIsOff and !isWakeKey"); + //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey"); return false; } event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE; @@ -6271,7 +6362,7 @@ public class WindowManagerService extends IWindowManager.Stub if (screenIsOff) { if (!mPolicy.isWakeAbsMovementTq(event.deviceId, device.classes, event)) { - //Log.i(TAG, "dropping because screenIsOff and !isWakeKey"); + //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey"); return false; } event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE; @@ -6292,7 +6383,7 @@ public class WindowManagerService extends IWindowManager.Stub case RawInputEvent.CLASS_KEYBOARD: KeyEvent ke = (KeyEvent)ev.event; if (mPolicy.isMovementKeyTi(ke.getKeyCode())) { - Log.w(TAG, "Dropping movement key during app switch: " + Slog.w(TAG, "Dropping movement key during app switch: " + ke.getKeyCode() + ", action=" + ke.getAction()); return FILTER_REMOVE; } @@ -6341,7 +6432,7 @@ public class WindowManagerService extends IWindowManager.Stub try { process(); } catch (Exception e) { - Log.e(TAG, "Exception in input dispatcher", e); + Slog.e(TAG, "Exception in input dispatcher", e); } } } @@ -6367,7 +6458,7 @@ public class WindowManagerService extends IWindowManager.Stub while (true) { long curTime = SystemClock.uptimeMillis(); - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "Waiting for next key: now=" + curTime + ", repeat @ " + nextKeyTime); @@ -6379,7 +6470,7 @@ public class WindowManagerService extends IWindowManager.Stub (int)((!configChanged && curTime < nextKeyTime) ? (nextKeyTime-curTime) : 0)); - if (DEBUG_INPUT && ev != null) Log.v( + if (DEBUG_INPUT && ev != null) Slog.v( TAG, "Event: type=" + ev.classType + " data=" + ev.event); if (MEASURE_LATENCY) { @@ -6441,7 +6532,7 @@ public class WindowManagerService extends IWindowManager.Stub lastKeyTime = curTime; nextKeyTime = lastKeyTime + ViewConfiguration.getLongPressTimeout(); - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "Received key down: first repeat @ " + nextKeyTime); } else { @@ -6450,7 +6541,7 @@ public class WindowManagerService extends IWindowManager.Stub // Arbitrary long timeout. lastKeyTime = curTime; nextKeyTime = curTime + LONG_WAIT; - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "Received key up: ignore repeat @ " + nextKeyTime); } @@ -6458,7 +6549,7 @@ public class WindowManagerService extends IWindowManager.Stub mQueue.recycleEvent(ev); break; case RawInputEvent.CLASS_TOUCHSCREEN: - //Log.i(TAG, "Read next event " + ev); + //Slog.i(TAG, "Read next event " + ev); dispatchPointer(ev, (MotionEvent)ev.event, 0, 0); break; case RawInputEvent.CLASS_TRACKBALL: @@ -6481,7 +6572,7 @@ public class WindowManagerService extends IWindowManager.Stub // Timeout occurred while key was down. If it is at or // past the key repeat time, dispatch the repeat. - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "Key timeout: repeat=" + nextKeyTime + ", now=" + curTime); if (curTime < nextKeyTime) { @@ -6491,7 +6582,7 @@ public class WindowManagerService extends IWindowManager.Stub lastKeyTime = nextKeyTime; nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY; keyRepeatCount++; - if (DEBUG_INPUT) Log.v( + if (DEBUG_INPUT) Slog.v( TAG, "Key repeat: count=" + keyRepeatCount + ", next @ " + nextKeyTime); KeyEvent newEvent; @@ -6516,7 +6607,7 @@ public class WindowManagerService extends IWindowManager.Stub } } catch (Exception e) { - Log.e(TAG, + Slog.e(TAG, "Input thread received uncaught exception: " + e, e); } } @@ -6604,7 +6695,7 @@ public class WindowManagerService extends IWindowManager.Stub } catch (RuntimeException e) { // Log all 'real' exceptions thrown to the caller if (!(e instanceof SecurityException)) { - Log.e(TAG, "Window Session Crash", e); + Slog.e(TAG, "Window Session Crash", e); } throw e; } @@ -6638,10 +6729,10 @@ public class WindowManagerService extends IWindowManager.Stub public int relayout(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, boolean insetsPending, Rect outFrame, Rect outContentInsets, - Rect outVisibleInsets, Surface outSurface) { + Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { return relayoutWindow(this, window, attrs, requestedWidth, requestedHeight, viewFlags, insetsPending, - outFrame, outContentInsets, outVisibleInsets, outSurface); + outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface); } public void setTransparentRegion(IWindow window, Region region) { @@ -6659,27 +6750,27 @@ public class WindowManagerService extends IWindowManager.Stub } public void finishDrawing(IWindow window) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "IWindow finishDrawing called for " + window); finishDrawingWindow(this, window); } public void finishKey(IWindow window) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "IWindow finishKey called for " + window); mKeyWaiter.finishedKey(this, window, false, KeyWaiter.RETURN_NOTHING); } public MotionEvent getPendingPointerMove(IWindow window) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "IWindow getPendingMotionEvent called for " + window); return mKeyWaiter.finishedKey(this, window, false, KeyWaiter.RETURN_PENDING_POINTER); } public MotionEvent getPendingTrackballMove(IWindow window) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "IWindow getPendingMotionEvent called for " + window); return mKeyWaiter.finishedKey(this, window, false, KeyWaiter.RETURN_PENDING_TRACKBALL); @@ -6703,7 +6794,8 @@ public class WindowManagerService extends IWindowManager.Stub long ident = Binder.clearCallingIdentity(); try { return mPolicy.performHapticFeedbackLw( - windowForClientLocked(this, window), effectId, always); + windowForClientLocked(this, window, true), + effectId, always); } finally { Binder.restoreCallingIdentity(ident); } @@ -6714,42 +6806,43 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { long ident = Binder.clearCallingIdentity(); try { - setWindowWallpaperPositionLocked(windowForClientLocked(this, window), + setWindowWallpaperPositionLocked( + windowForClientLocked(this, window, true), x, y, xStep, yStep); } finally { Binder.restoreCallingIdentity(ident); } } } - + public void wallpaperOffsetsComplete(IBinder window) { WindowManagerService.this.wallpaperOffsetsComplete(window); } - + public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, int z, Bundle extras, boolean sync) { synchronized(mWindowMap) { long ident = Binder.clearCallingIdentity(); try { return sendWindowWallpaperCommandLocked( - windowForClientLocked(this, window), + windowForClientLocked(this, window, true), action, x, y, z, extras, sync); } finally { Binder.restoreCallingIdentity(ident); } } } - + public void wallpaperCommandComplete(IBinder window, Bundle result) { WindowManagerService.this.wallpaperCommandComplete(window, result); } - + void windowAddedLocked() { if (mSurfaceSession == null) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "First window added to " + this + ", creating SurfaceSession"); mSurfaceSession = new SurfaceSession(); - if (SHOW_TRANSACTIONS) Log.i( + if (SHOW_TRANSACTIONS) Slog.i( TAG, " NEW SURFACE SESSION " + mSurfaceSession); mSessions.add(this); } @@ -6765,15 +6858,15 @@ public class WindowManagerService extends IWindowManager.Stub if (mNumWindow <= 0 && mClientDead) { mSessions.remove(this); if (mSurfaceSession != null) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Last window removed from " + this + ", destroying " + mSurfaceSession); - if (SHOW_TRANSACTIONS) Log.i( + if (SHOW_TRANSACTIONS) Slog.i( TAG, " KILL SURFACE SESSION " + mSurfaceSession); try { mSurfaceSession.kill(); } catch (Exception e) { - Log.w(TAG, "Exception thrown when killing surface session " + Slog.w(TAG, "Exception thrown when killing surface session " + mSurfaceSession + " in session " + this + ": " + e.toString()); } @@ -6848,11 +6941,21 @@ public class WindowManagerService extends IWindowManager.Stub WindowState mNextOutsideTouch; + int mLayoutSeq = -1; + + Configuration mConfiguration = null; + // Actual frame shown on-screen (may be modified by animation) final Rect mShownFrame = new Rect(); final Rect mLastShownFrame = new Rect(); /** + * Set when we have changed the size of the surface, to know that + * we must tell them application to resize (and thus redraw itself). + */ + boolean mSurfaceResized; + + /** * Insets that determine the actually visible area */ final Rect mVisibleInsets = new Rect(); @@ -6938,7 +7041,7 @@ public class WindowManagerService extends IWindowManager.Stub // Wallpaper windows: pixels offset based on above variables. int mXOffset; int mYOffset; - + // This is set after IWindowSession.relayout() has been called at // least once for the window. It allows us to detect the situation // where we don't yet have a surface, but should have one soon, so @@ -6979,6 +7082,12 @@ public class WindowManagerService extends IWindowManager.Stub // Is this window now (or just being) removed? boolean mRemoved; + // For debugging, this is the last information given to the surface flinger. + boolean mSurfaceShown; + int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH; + int mSurfaceLayer; + float mSurfaceAlpha; + WindowState(Session s, IWindow c, WindowToken token, WindowState attachedWindow, WindowManager.LayoutParams a, int viewVisibility) { @@ -6989,7 +7098,7 @@ public class WindowManagerService extends IWindowManager.Stub mViewVisibility = viewVisibility; DeathRecipient deathRecipient = new DeathRecipient(); mAlpha = a.alpha; - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Window " + this + " client=" + c.asBinder() + " token=" + token + " (" + mAttrs.token + ")"); try { @@ -7066,7 +7175,7 @@ public class WindowManagerService extends IWindowManager.Stub } void attach() { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Attaching " + this + " token=" + mToken + ", list=" + mToken.windows); mSession.windowAddedLocked(); @@ -7096,8 +7205,8 @@ public class WindowManagerService extends IWindowManager.Stub w = mAttrs.width < 0 ? pw : mAttrs.width; h = mAttrs.height< 0 ? ph : mAttrs.height; } else { - w = mAttrs.width == mAttrs.FILL_PARENT ? pw : mRequestedWidth; - h = mAttrs.height== mAttrs.FILL_PARENT ? ph : mRequestedHeight; + w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth; + h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight; } final Rect content = mContentFrame; @@ -7121,7 +7230,7 @@ public class WindowManagerService extends IWindowManager.Stub // Now make sure the window fits in the overall display. Gravity.applyDisplay(mAttrs.gravity, df, frame); - + // Make sure the content and visible frames are inside of the // final window frame. if (content.left < frame.left) content.left = frame.left; @@ -7149,11 +7258,11 @@ public class WindowManagerService extends IWindowManager.Stub updateWallpaperOffsetLocked(this, mDisplay.getWidth(), mDisplay.getHeight(), false); } - + if (localLOGV) { //if ("com.google.android.youtube".equals(mAttrs.packageName) // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { - Log.v(TAG, "Resolving (mRequestedWidth=" + Slog.v(TAG, "Resolving (mRequestedWidth=" + mRequestedWidth + ", mRequestedheight=" + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph + "): frame=" + mFrame.toShortString() @@ -7211,16 +7320,8 @@ public class WindowManagerService extends IWindowManager.Stub return mAppToken != null ? mAppToken.firstWindowDrawn : false; } - public boolean hasAppStartingIcon() { - return mAppToken != null ? (mAppToken.startingData != null) : false; - } - - public WindowManagerPolicy.WindowState getAppStartingWindow() { - return mAppToken != null ? mAppToken.startingWindow : null; - } - public void setAnimation(Animation anim) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Setting animation in " + this + ": " + anim); mAnimating = false; mLocalAnimating = false; @@ -7256,7 +7357,7 @@ public class WindowManagerService extends IWindowManager.Stub if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) { flags |= Surface.SECURE; } - if (DEBUG_VISIBILITY) Log.v( + if (DEBUG_VISIBILITY) Slog.v( TAG, "Creating surface in session " + mSession.mSurfaceSession + " window " + this + " w=" + mFrame.width() @@ -7277,60 +7378,70 @@ public class WindowManagerService extends IWindowManager.Stub if (w <= 0) w = 1; if (h <= 0) h = 1; + mSurfaceShown = false; + mSurfaceLayer = 0; + mSurfaceAlpha = 1; + mSurfaceX = 0; + mSurfaceY = 0; + mSurfaceW = w; + mSurfaceH = h; try { mSurface = new Surface( mSession.mSurfaceSession, mSession.mPid, + mAttrs.getTitle().toString(), 0, w, h, mAttrs.format, flags); - if (SHOW_TRANSACTIONS) Log.i(TAG, " CREATE SURFACE " + if (SHOW_TRANSACTIONS) Slog.i(TAG, " CREATE SURFACE " + mSurface + " IN SESSION " + mSession.mSurfaceSession + ": pid=" + mSession.mPid + " format=" + mAttrs.format + " flags=0x" - + Integer.toHexString(flags)); + + Integer.toHexString(flags) + + " / " + this); } catch (Surface.OutOfResourcesException e) { - Log.w(TAG, "OutOfResourcesException creating surface"); + Slog.w(TAG, "OutOfResourcesException creating surface"); reclaimSomeSurfaceMemoryLocked(this, "create"); return null; } catch (Exception e) { - Log.e(TAG, "Exception creating surface", e); + Slog.e(TAG, "Exception creating surface", e); return null; } - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Got surface: " + mSurface + ", set left=" + mFrame.left + " top=" + mFrame.top + ", animLayer=" + mAnimLayer); if (SHOW_TRANSACTIONS) { - Log.i(TAG, ">>> OPEN TRANSACTION"); - Log.i(TAG, " SURFACE " + mSurface + ": CREATE (" - + mAttrs.getTitle() + ") pos=(" + - mFrame.left + "," + mFrame.top + ") (" + - mFrame.width() + "x" + mFrame.height() + "), layer=" + - mAnimLayer + " HIDE"); + Slog.i(TAG, ">>> OPEN TRANSACTION"); + if (SHOW_TRANSACTIONS) logSurface(this, + "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" + + mFrame.width() + "x" + mFrame.height() + "), layer=" + + mAnimLayer + " HIDE", null); } Surface.openTransaction(); try { try { - mSurface.setPosition(mFrame.left + mXOffset, - mFrame.top + mYOffset); + mSurfaceX = mFrame.left + mXOffset; + mSurfaceY = mFrame.top + mYOffset; + mSurface.setPosition(mSurfaceX, mSurfaceY); + mSurfaceLayer = mAnimLayer; mSurface.setLayer(mAnimLayer); + mSurfaceShown = false; mSurface.hide(); if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) { - if (SHOW_TRANSACTIONS) Log.i(TAG, " SURFACE " - + mSurface + ": DITHER"); + if (SHOW_TRANSACTIONS) logSurface(this, "DITHER", null); mSurface.setFlags(Surface.SURFACE_DITHER, Surface.SURFACE_DITHER); } } catch (RuntimeException e) { - Log.w(TAG, "Error creating surface in " + w, e); + Slog.w(TAG, "Error creating surface in " + w, e); reclaimSomeSurfaceMemoryLocked(this, "create-init"); } mLastHidden = true; } finally { - if (SHOW_TRANSACTIONS) Log.i(TAG, "<<< CLOSE TRANSACTION"); + if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION"); Surface.closeTransaction(); } - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Created surface " + this); } return mSurface; @@ -7360,7 +7471,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowState c = (WindowState)mChildWindows.get(i); c.mAttachedHidden = true; } - + if (mReportDestroySurface) { mReportDestroySurface = false; mSurfacePendingDestroy = true; @@ -7371,34 +7482,40 @@ public class WindowManagerService extends IWindowManager.Stub } catch (RemoteException e) { } } - + try { if (DEBUG_VISIBILITY) { - RuntimeException e = new RuntimeException(); - if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); - Log.w(TAG, "Window " + this + " destroying surface " + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.w(TAG, "Window " + this + " destroying surface " + mSurface + ", session " + mSession, e); } if (SHOW_TRANSACTIONS) { - RuntimeException ex = new RuntimeException(); - if (!HIDE_STACK_CRAWLS) ex.fillInStackTrace(); - Log.i(TAG, " SURFACE " + mSurface + ": DESTROY (" - + mAttrs.getTitle() + ")", ex); + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + if (SHOW_TRANSACTIONS) logSurface(this, "DESTROY", e); } mSurface.destroy(); } catch (RuntimeException e) { - Log.w(TAG, "Exception thrown when destroying Window " + this + Slog.w(TAG, "Exception thrown when destroying Window " + this + " surface " + mSurface + " session " + mSession + ": " + e.toString()); } - + + mSurfaceShown = false; mSurface = null; } } boolean finishDrawingLocked() { if (mDrawPending) { - if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Log.v( + if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Slog.v( TAG, "finishDrawingLocked: " + mSurface); mCommitDrawPending = true; mDrawPending = false; @@ -7409,7 +7526,7 @@ public class WindowManagerService extends IWindowManager.Stub // This must be called while inside a transaction. boolean commitFinishDrawingLocked(long currentTime) { - //Log.i(TAG, "commitFinishDrawingLocked: " + mSurface); + //Slog.i(TAG, "commitFinishDrawingLocked: " + mSurface); if (!mCommitDrawPending) { return false; } @@ -7426,16 +7543,19 @@ public class WindowManagerService extends IWindowManager.Stub // This must be called while inside a transaction. boolean performShowLocked() { if (DEBUG_VISIBILITY) { - RuntimeException e = new RuntimeException(); - if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); - Log.v(TAG, "performShow on " + this + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.v(TAG, "performShow on " + this + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay() + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e); } if (mReadyToShow && isReadyForDisplay()) { - if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Log.i( - TAG, " SURFACE " + mSurface + ": SHOW (performShowLocked)"); - if (DEBUG_VISIBILITY) Log.v(TAG, "Showing " + this + if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) logSurface(this, + "SHOW (performShowLocked)", null); + if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this + " during animation: policyVis=" + mPolicyVisibility + " attHidden=" + mAttachedHidden + " tok.hiddenRequested=" @@ -7460,24 +7580,26 @@ public class WindowManagerService extends IWindowManager.Stub while (i > 0) { i--; WindowState c = (WindowState)mChildWindows.get(i); - if (c.mSurface != null && c.mAttachedHidden) { + if (c.mAttachedHidden) { c.mAttachedHidden = false; - c.performShowLocked(); - // It hadn't been shown, which means layout not - // performed on it, so now we want to make sure to - // do a layout. If called from within the transaction - // loop, this will cause it to restart with a new - // layout. - mLayoutNeeded = true; + if (c.mSurface != null) { + c.performShowLocked(); + // It hadn't been shown, which means layout not + // performed on it, so now we want to make sure to + // do a layout. If called from within the transaction + // loop, this will cause it to restart with a new + // layout. + mLayoutNeeded = true; + } } } if (mAttrs.type != TYPE_APPLICATION_STARTING && mAppToken != null) { mAppToken.firstWindowDrawn = true; - + if (mAppToken.startingData != null) { - if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Log.v(TAG, + if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, "Finish starting " + mToken + ": first real window is shown, no animation"); // If this initial window is animating, stop it -- we @@ -7501,14 +7623,14 @@ public class WindowManagerService extends IWindowManager.Stub // This must be called while inside a transaction. Returns true if // there is more animation to run. boolean stepAnimationLocked(long currentTime, int dw, int dh) { - if (!mDisplayFrozen) { + if (!mDisplayFrozen && mPolicy.isScreenOn()) { // We will run animations as long as the display isn't frozen. if (!mDrawPending && !mCommitDrawPending && mAnimation != null) { mHasTransformation = true; mHasLocalTransformation = true; if (!mLocalAnimating) { - if (DEBUG_ANIM) Log.v( + if (DEBUG_ANIM) Slog.v( TAG, "Starting animation in " + this + " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + " dw=" + dw + " dh=" + dh + " scale=" + mWindowAnimationScale); @@ -7520,14 +7642,14 @@ public class WindowManagerService extends IWindowManager.Stub mTransformation.clear(); final boolean more = mAnimation.getTransformation( currentTime, mTransformation); - if (DEBUG_ANIM) Log.v( + if (DEBUG_ANIM) Slog.v( TAG, "Stepped animation in " + this + ": more=" + more + ", xform=" + mTransformation); if (more) { // we're not done! return true; } - if (DEBUG_ANIM) Log.v( + if (DEBUG_ANIM) Slog.v( TAG, "Finished animation in " + this + " @ " + currentTime); mAnimation = null; @@ -7566,7 +7688,7 @@ public class WindowManagerService extends IWindowManager.Stub return false; } - if (DEBUG_ANIM) Log.v( + if (DEBUG_ANIM) Slog.v( TAG, "Animation done in " + this + ": exiting=" + mExiting + ", reportedVisible=" + (mAppToken != null ? mAppToken.reportedVisible : false)); @@ -7580,16 +7702,25 @@ public class WindowManagerService extends IWindowManager.Stub } else if (mIsWallpaper) { mAnimLayer += mWallpaperAnimLayerAdjustment; } - if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this + if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer); mHasTransformation = false; mHasLocalTransformation = false; - mPolicyVisibility = mPolicyVisibilityAfterAnim; - if (!mPolicyVisibility) { - // Window is no longer visible -- make sure if we were waiting - // for it to be displayed before enabling the display, that - // we allow the display to be enabled now. - enableScreenIfNeededLocked(); + if (mPolicyVisibility != mPolicyVisibilityAfterAnim) { + if (DEBUG_VISIBILITY) { + Slog.v(TAG, "Policy visibility changing after anim in " + this + ": " + + mPolicyVisibilityAfterAnim); + } + mPolicyVisibility = mPolicyVisibilityAfterAnim; + if (!mPolicyVisibility) { + if (mCurrentFocus == this) { + mFocusMayChange = true; + } + // Window is no longer visible -- make sure if we were waiting + // for it to be displayed before enabling the display, that + // we allow the display to be enabled now. + enableScreenIfNeededLocked(); + } } mTransformation.clear(); if (mHasDrawn @@ -7597,7 +7728,7 @@ public class WindowManagerService extends IWindowManager.Stub && mAppToken != null && mAppToken.firstWindowDrawn && mAppToken.startingData != null) { - if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Finish starting " + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Finish starting " + mToken + ": first real window done animating"); mFinishedStarting.add(mAppToken); mH.sendEmptyMessage(H.FINISHED_STARTING); @@ -7613,7 +7744,7 @@ public class WindowManagerService extends IWindowManager.Stub } void finishExit() { - if (DEBUG_ANIM) Log.v( + if (DEBUG_ANIM) Slog.v( TAG, "finishExit in " + this + ": exiting=" + mExiting + " remove=" + mRemoveOnExit @@ -7632,18 +7763,18 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Exit animation finished in " + this + ": remove=" + mRemoveOnExit); if (mSurface != null) { mDestroySurface.add(this); mDestroying = true; - if (SHOW_TRANSACTIONS) Log.i( - TAG, " SURFACE " + mSurface + ": HIDE (finishExit)"); + if (SHOW_TRANSACTIONS) logSurface(this, "HIDE (finishExit)", null); + mSurfaceShown = false; try { mSurface.hide(); } catch (RuntimeException e) { - Log.w(TAG, "Error hiding surface in " + this, e); + Slog.w(TAG, "Error hiding surface in " + this, e); } mLastHidden = true; mKeyWaiter.releasePendingPointerLocked(mSession); @@ -7671,7 +7802,7 @@ public class WindowManagerService extends IWindowManager.Stub Transformation appTransformation = (mAppToken != null && mAppToken.hasTransformation) ? mAppToken.transformation : null; - + // Wallpapers are animated based on the "real" window they // are currently targeting. if (mAttrs.type == TYPE_WALLPAPER && mLowerWallpaperTarget == null @@ -7681,7 +7812,7 @@ public class WindowManagerService extends IWindowManager.Stub !mWallpaperTarget.mAnimation.getDetachWallpaper()) { attachedTransformation = mWallpaperTarget.mTransformation; if (DEBUG_WALLPAPER && attachedTransformation != null) { - Log.v(TAG, "WP target attached xform: " + attachedTransformation); + Slog.v(TAG, "WP target attached xform: " + attachedTransformation); } } if (mWallpaperTarget.mAppToken != null && @@ -7690,11 +7821,11 @@ public class WindowManagerService extends IWindowManager.Stub !mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) { appTransformation = mWallpaperTarget.mAppToken.transformation; if (DEBUG_WALLPAPER && appTransformation != null) { - Log.v(TAG, "WP target app xform: " + appTransformation); + Slog.v(TAG, "WP target app xform: " + appTransformation); } } } - + if (selfTransformation || attachedTransformation != null || appTransformation != null) { // cache often used attributes locally @@ -7719,7 +7850,7 @@ public class WindowManagerService extends IWindowManager.Stub // (a 2x2 matrix + an offset) // Here we must not transform the position of the surface // since it is already included in the transformation. - //Log.i(TAG, "Transform: " + matrix); + //Slog.i(TAG, "Transform: " + matrix); tmpMatrix.getValues(tmpFloats); mDsDx = tmpFloats[Matrix.MSCALE_X]; @@ -7742,7 +7873,7 @@ public class WindowManagerService extends IWindowManager.Stub || (!PixelFormat.formatHasAlpha(mAttrs.format) || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) && x == frame.left && y == frame.top))) { - //Log.i(TAG, "Applying alpha transform"); + //Slog.i(TAG, "Applying alpha transform"); if (selfTransformation) { mShownAlpha *= mTransformation.getAlpha(); } @@ -7753,10 +7884,10 @@ public class WindowManagerService extends IWindowManager.Stub mShownAlpha *= appTransformation.getAlpha(); } } else { - //Log.i(TAG, "Not applying alpha transform"); + //Slog.i(TAG, "Not applying alpha transform"); } - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Continuing animation in " + this + ": " + mShownFrame + ", alpha=" + mTransformation.getAlpha()); @@ -7798,6 +7929,7 @@ public class WindowManagerService extends IWindowManager.Stub final AppWindowToken atoken = mAppToken; return mSurface != null && !mAttachedHidden && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested) + && (mOrientationChanging || (!mDrawPending && !mCommitDrawPending)) && !mExiting && !mDestroying; } @@ -7901,12 +8033,14 @@ public class WindowManagerService extends IWindowManager.Stub /** * Returns true if the window has a surface that it has drawn a - * complete UI in to. + * complete UI in to. Note that this returns true if the orientation + * is changing even if the window hasn't redrawn because we don't want + * to stop things from executing during that time. */ public boolean isDrawnLw() { final AppWindowToken atoken = mAppToken; return mSurface != null && !mDestroying - && !mDrawPending && !mCommitDrawPending; + && (mOrientationChanging || (!mDrawPending && !mCommitDrawPending)); } public boolean fillsScreenLw(int screenWidth, int screenHeight, @@ -7984,8 +8118,8 @@ public class WindowManagerService extends IWindowManager.Stub public void binderDied() { try { synchronized(mWindowMap) { - WindowState win = windowForClientLocked(mSession, mClient); - Log.i(TAG, "WIN DEATH: " + win); + WindowState win = windowForClientLocked(mSession, mClient, false); + Slog.i(TAG, "WIN DEATH: " + win); if (win != null) { removeWindowLocked(mSession, win); } @@ -8016,6 +8150,19 @@ public class WindowManagerService extends IWindowManager.Stub if (mPolicyVisibility && mPolicyVisibilityAfterAnim) { return false; } + if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this); + if (doAnimation) { + if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility=" + + mPolicyVisibility + " mAnimation=" + mAnimation); + if (mDisplayFrozen || !mPolicy.isScreenOn()) { + doAnimation = false; + } else if (mPolicyVisibility && mAnimation == null) { + // Check for the case where we are currently visible and + // not animating; we do not want to do animation at such a + // point to become visible when we already are. + doAnimation = false; + } + } mPolicyVisibility = true; mPolicyVisibilityAfterAnim = true; if (doAnimation) { @@ -8032,6 +8179,11 @@ public class WindowManagerService extends IWindowManager.Stub } boolean hideLw(boolean doAnimation, boolean requestAnim) { + if (doAnimation) { + if (mDisplayFrozen || !mPolicy.isScreenOn()) { + doAnimation = false; + } + } boolean current = doAnimation ? mPolicyVisibilityAfterAnim : mPolicyVisibility; if (!current) { @@ -8046,12 +8198,16 @@ public class WindowManagerService extends IWindowManager.Stub if (doAnimation) { mPolicyVisibilityAfterAnim = false; } else { + if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this); mPolicyVisibilityAfterAnim = false; mPolicyVisibility = false; // Window is no longer visible -- make sure if we were waiting // for it to be displayed before enabling the display, that // we allow the display to be enabled now. enableScreenIfNeededLocked(); + if (mCurrentFocus == this) { + mFocusMayChange = true; + } } if (requestAnim) { requestAnimationLocked(0); @@ -8060,8 +8216,6 @@ public class WindowManagerService extends IWindowManager.Stub } void dump(PrintWriter pw, String prefix) { - StringBuilder sb = new StringBuilder(64); - pw.print(prefix); pw.print("mSession="); pw.print(mSession); pw.print(" mClient="); pw.println(mClient.asBinder()); pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs); @@ -8084,6 +8238,13 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mLastLayer="); pw.println(mLastLayer); if (mSurface != null) { pw.print(prefix); pw.print("mSurface="); pw.println(mSurface); + pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown); + pw.print(" layer="); pw.print(mSurfaceLayer); + pw.print(" alpha="); pw.print(mSurfaceAlpha); + pw.print(" rect=("); pw.print(mSurfaceX); + pw.print(","); pw.print(mSurfaceY); + pw.print(") "); pw.print(mSurfaceW); + pw.print(" x "); pw.println(mSurfaceH); } pw.print(prefix); pw.print("mToken="); pw.println(mToken); pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken); @@ -8109,7 +8270,8 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(prefix); pw.print("mRelayoutCalled="); pw.println(mRelayoutCalled); } pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); - pw.print(" h="); pw.println(mRequestedHeight); + pw.print(" h="); pw.print(mRequestedHeight); + pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); if (mXOffset != 0 || mYOffset != 0) { pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset); pw.print(" y="); pw.println(mYOffset); @@ -8123,6 +8285,7 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets); pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending); } + pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration); pw.print(prefix); pw.print("mShownFrame="); mShownFrame.printShortString(pw); pw.print(" last="); mLastShownFrame.printShortString(pw); @@ -8236,19 +8399,19 @@ public class WindowManagerService extends IWindowManager.Stub // Set to true when this token is in a pending transaction where it // will be shown. boolean waitingToShow; - + // Set to true when this token is in a pending transaction where it // will be hidden. boolean waitingToHide; - + // Set to true when this token is in a pending transaction where its // windows will be put to the bottom of the list. boolean sendingToBottom; - + // Set to true when this token is in a pending transaction where its // windows will be put to the top of the list. boolean sendingToTop; - + WindowToken(IBinder _token, int type, boolean _explicit) { token = _token; windowType = type; @@ -8350,7 +8513,7 @@ public class WindowManagerService extends IWindowManager.Stub } public void setAnimation(Animation anim) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Setting animation in " + this + ": " + anim); animation = anim; animating = false; @@ -8372,7 +8535,7 @@ public class WindowManagerService extends IWindowManager.Stub public void setDummyAnimation() { if (animation == null) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Setting dummy animation in " + this); animation = sDummyAnimation; } @@ -8391,7 +8554,7 @@ public class WindowManagerService extends IWindowManager.Stub 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 + ": " + if (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": " + w.mAnimLayer); if (w == mInputMethodTarget) { setInputMethodAnimLayerAdjustment(adj); @@ -8411,7 +8574,7 @@ public class WindowManagerService extends IWindowManager.Stub continue; } try { - if (DEBUG_VISIBILITY) Log.v(TAG, + if (DEBUG_VISIBILITY) Slog.v(TAG, "Setting visibility of " + win + ": " + (!clientHidden)); win.mClient.dispatchAppVisibility(!clientHidden); } catch (RemoteException e) { @@ -8423,7 +8586,7 @@ public class WindowManagerService extends IWindowManager.Stub final int NW = allAppWindows.size(); for (int i=0; i<NW; i++) { WindowState w = allAppWindows.get(i); - if (DEBUG_VISIBILITY) Log.v(TAG, + if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + w); w.performShowLocked(); } @@ -8431,7 +8594,7 @@ public class WindowManagerService extends IWindowManager.Stub // This must be called while inside a transaction. boolean stepAnimationLocked(long currentTime, int dw, int dh) { - if (!mDisplayFrozen) { + if (!mDisplayFrozen && mPolicy.isScreenOn()) { // We will run animations as long as the display isn't frozen. if (animation == sDummyAnimation) { @@ -8444,7 +8607,7 @@ public class WindowManagerService extends IWindowManager.Stub if ((allDrawn || animating || startingDisplayed) && animation != null) { if (!animating) { - if (DEBUG_ANIM) Log.v( + if (DEBUG_ANIM) Slog.v( TAG, "Starting animation in " + this + " @ " + currentTime + ": dw=" + dw + " dh=" + dh + " scale=" + mTransitionAnimationScale @@ -8456,7 +8619,7 @@ public class WindowManagerService extends IWindowManager.Stub transformation.clear(); final boolean more = animation.getTransformation( currentTime, transformation); - if (DEBUG_ANIM) Log.v( + if (DEBUG_ANIM) Slog.v( TAG, "Stepped animation in " + this + ": more=" + more + ", xform=" + transformation); if (more) { @@ -8464,7 +8627,7 @@ public class WindowManagerService extends IWindowManager.Stub hasTransformation = true; return true; } - if (DEBUG_ANIM) Log.v( + if (DEBUG_ANIM) Slog.v( TAG, "Finished animation in " + this + " @ " + currentTime); animation = null; @@ -8488,7 +8651,7 @@ public class WindowManagerService extends IWindowManager.Stub moveInputMethodWindowsIfNeededLocked(true); } - if (DEBUG_ANIM) Log.v( + if (DEBUG_ANIM) Slog.v( TAG, "Animation done in " + this + ": reportedVisible=" + reportedVisible); @@ -8516,20 +8679,21 @@ public class WindowManagerService extends IWindowManager.Stub int numVisible = 0; boolean nowGone = true; - if (DEBUG_VISIBILITY) Log.v(TAG, "Update reported visibility: " + this); + if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this); final int N = allAppWindows.size(); for (int i=0; i<N; i++) { WindowState win = allAppWindows.get(i); if (win == startingWindow || win.mAppFreezing - || win.mAttrs.type == TYPE_APPLICATION_STARTING) { + || win.mViewVisibility != View.VISIBLE + || win.mAttrs.type == TYPE_APPLICATION_STARTING) { continue; } if (DEBUG_VISIBILITY) { - Log.v(TAG, "Win " + win + ": isDrawn=" + Slog.v(TAG, "Win " + win + ": isDrawn=" + win.isDrawnLw() + ", isAnimating=" + win.isAnimating()); if (!win.isDrawnLw()) { - Log.v(TAG, "Not displayed: s=" + win.mSurface + Slog.v(TAG, "Not displayed: s=" + win.mSurface + " pv=" + win.mPolicyVisibility + " dp=" + win.mDrawPending + " cdp=" + win.mCommitDrawPending @@ -8552,10 +8716,10 @@ public class WindowManagerService extends IWindowManager.Stub } boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting; - if (DEBUG_VISIBILITY) Log.v(TAG, "VIS " + this + ": interesting=" + if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting=" + numInteresting + " visible=" + numVisible); if (nowVisible != reportedVisible) { - if (DEBUG_VISIBILITY) Log.v( + if (DEBUG_VISIBILITY) Slog.v( TAG, "Visibility changed in " + this + ": vis=" + nowVisible); reportedVisible = nowVisible; @@ -8580,7 +8744,7 @@ public class WindowManagerService extends IWindowManager.Stub } return null; } - + void dump(PrintWriter pw, String prefix) { super.dump(pw, prefix); if (appToken != null) { @@ -8697,7 +8861,7 @@ public class WindowManagerService extends IWindowManager.Stub 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; + public static final int SEND_NEW_CONFIGURATION = 18; private Session mLastReportedHold; @@ -8719,11 +8883,11 @@ public class WindowManagerService extends IWindowManager.Stub return; } mLastFocus = newFocus; - //Log.i(TAG, "Focus moving from " + lastFocus + //Slog.i(TAG, "Focus moving from " + lastFocus // + " to " + newFocus); if (newFocus != null && lastFocus != null && !newFocus.isDisplayedLw()) { - //Log.i(TAG, "Delaying loss of focus..."); + //Slog.i(TAG, "Delaying loss of focus..."); mLosingFocus.add(lastFocus); lastFocus = null; } @@ -8734,7 +8898,7 @@ public class WindowManagerService extends IWindowManager.Stub // + " to " + newFocus); if (newFocus != null) { try { - //Log.i(TAG, "Gaining focus: " + newFocus); + //Slog.i(TAG, "Gaining focus: " + newFocus); newFocus.mClient.windowFocusChanged(true, mInTouchMode); } catch (RemoteException e) { // Ignore if process has died. @@ -8743,7 +8907,7 @@ public class WindowManagerService extends IWindowManager.Stub if (lastFocus != null) { try { - //Log.i(TAG, "Losing focus: " + lastFocus); + //Slog.i(TAG, "Losing focus: " + lastFocus); lastFocus.mClient.windowFocusChanged(false, mInTouchMode); } catch (RemoteException e) { // Ignore if process has died. @@ -8763,7 +8927,7 @@ public class WindowManagerService extends IWindowManager.Stub final int N = losers.size(); for (int i=0; i<N; i++) { try { - //Log.i(TAG, "Losing delayed focus: " + losers.get(i)); + //Slog.i(TAG, "Losing delayed focus: " + losers.get(i)); losers.get(i).mClient.windowFocusChanged(false, mInTouchMode); } catch (RemoteException e) { // Ignore if process has died. @@ -8787,7 +8951,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Add starting " + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Add starting " + wtoken + ": pkg=" + sd.pkg); View view = null; @@ -8797,7 +8961,7 @@ public class WindowManagerService extends IWindowManager.Stub sd.theme, sd.nonLocalizedLabel, sd.labelRes, sd.icon); } catch (Exception e) { - Log.w(TAG, "Exception when adding starting window", e); + Slog.w(TAG, "Exception when adding starting window", e); } if (view != null) { @@ -8808,7 +8972,7 @@ public class WindowManagerService extends IWindowManager.Stub // If the window was successfully added, then // we need to remove it. if (wtoken.startingWindow != null) { - if (DEBUG_STARTING_WINDOW) Log.v(TAG, + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Aborted starting " + wtoken + ": removed=" + wtoken.removed + " startingData=" + wtoken.startingData); @@ -8819,7 +8983,7 @@ public class WindowManagerService extends IWindowManager.Stub } else { wtoken.startingView = view; } - if (DEBUG_STARTING_WINDOW && !abort) Log.v(TAG, + if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG, "Added starting " + wtoken + ": startingWindow=" + wtoken.startingWindow + " startingView=" @@ -8830,7 +8994,7 @@ public class WindowManagerService extends IWindowManager.Stub try { mPolicy.removeStartingWindow(wtoken.token, view); } catch (Exception e) { - Log.w(TAG, "Exception when removing starting window", e); + Slog.w(TAG, "Exception when removing starting window", e); } } } @@ -8841,7 +9005,7 @@ public class WindowManagerService extends IWindowManager.Stub IBinder token = null; View view = null; synchronized (mWindowMap) { - if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Remove starting " + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Remove starting " + wtoken + ": startingWindow=" + wtoken.startingWindow + " startingView=" + wtoken.startingView); @@ -8857,7 +9021,7 @@ public class WindowManagerService extends IWindowManager.Stub try { mPolicy.removeStartingWindow(token, view); } catch (Exception e) { - Log.w(TAG, "Exception when removing starting window", e); + Slog.w(TAG, "Exception when removing starting window", e); } } } break; @@ -8873,7 +9037,7 @@ public class WindowManagerService extends IWindowManager.Stub } AppWindowToken wtoken = mFinishedStarting.remove(N-1); - if (DEBUG_STARTING_WINDOW) Log.v(TAG, + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Finished starting " + wtoken + ": startingWindow=" + wtoken.startingWindow + " startingView=" + wtoken.startingView); @@ -8892,7 +9056,7 @@ public class WindowManagerService extends IWindowManager.Stub try { mPolicy.removeStartingWindow(token, view); } catch (Exception e) { - Log.w(TAG, "Exception when removing starting window", e); + Slog.w(TAG, "Exception when removing starting window", e); } } } break; @@ -8904,7 +9068,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean nowGone = msg.arg2 != 0; try { - if (DEBUG_VISIBILITY) Log.v( + if (DEBUG_VISIBILITY) Slog.v( TAG, "Reporting visible in " + wtoken + " visible=" + nowVisible + " gone=" + nowGone); @@ -8919,14 +9083,14 @@ public class WindowManagerService extends IWindowManager.Stub case WINDOW_FREEZE_TIMEOUT: { synchronized (mWindowMap) { - Log.w(TAG, "Window freeze timeout expired."); + Slog.w(TAG, "Window freeze timeout expired."); int i = mWindows.size(); while (i > 0) { i--; WindowState w = (WindowState)mWindows.get(i); if (w.mOrientationChanging) { w.mOrientationChanging = false; - Log.w(TAG, "Force clearing orientation change: " + w); + Slog.w(TAG, "Force clearing orientation change: " + w); } } performLayoutAndPlaceSurfacesLocked(); @@ -8964,7 +9128,7 @@ public class WindowManagerService extends IWindowManager.Stub case APP_TRANSITION_TIMEOUT: { synchronized (mWindowMap) { if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** APP TRANSITION TIMEOUT"); mAppTransitionReady = true; mAppTransitionTimeout = true; @@ -9009,13 +9173,13 @@ public class WindowManagerService extends IWindowManager.Stub case APP_FREEZE_TIMEOUT: { synchronized (mWindowMap) { - Log.w(TAG, "App freeze timeout expired."); + Slog.w(TAG, "App freeze timeout expired."); int i = mAppTokens.size(); while (i > 0) { i--; AppWindowToken tok = mAppTokens.get(i); if (tok.freezingScreen) { - Log.w(TAG, "Force clearing freeze: " + tok); + Slog.w(TAG, "Force clearing freeze: " + tok); unsetAppFreezingScreenLocked(tok, true, true); } } @@ -9023,10 +9187,9 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case COMPUTE_AND_SEND_NEW_CONFIGURATION: { - if (updateOrientationFromAppTokensUnchecked(null, null) != null) { - sendNewConfiguration(); - } + case SEND_NEW_CONFIGURATION: { + removeMessages(SEND_NEW_CONFIGURATION); + sendNewConfiguration(); break; } @@ -9068,23 +9231,33 @@ public class WindowManagerService extends IWindowManager.Stub // Internals // ------------------------------------------------------------- - final WindowState windowForClientLocked(Session session, IWindow client) { - return windowForClientLocked(session, client.asBinder()); + final WindowState windowForClientLocked(Session session, IWindow client, + boolean throwOnError) { + return windowForClientLocked(session, client.asBinder(), throwOnError); } - final WindowState windowForClientLocked(Session session, IBinder client) { + final WindowState windowForClientLocked(Session session, IBinder client, + boolean throwOnError) { WindowState win = mWindowMap.get(client); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Looking up client " + client + ": " + win); if (win == null) { - RuntimeException ex = new RuntimeException(); - Log.w(TAG, "Requested window " + client + " does not exist", ex); + RuntimeException ex = new IllegalArgumentException( + "Requested window " + client + " does not exist"); + if (throwOnError) { + throw ex; + } + Slog.w(TAG, "Failed looking up window", ex); return null; } if (session != null && win.mSession != session) { - RuntimeException ex = new RuntimeException(); - Log.w(TAG, "Requested window " + client + " is in session " + - win.mSession + ", not " + session, ex); + RuntimeException ex = new IllegalArgumentException( + "Requested window " + client + " is in session " + + win.mSession + ", not " + session); + if (throwOnError) { + throw ex; + } + Slog.w(TAG, "Failed looking up window", ex); return null; } @@ -9096,14 +9269,14 @@ public class WindowManagerService extends IWindowManager.Stub int i; int lastWallpaper = -1; int numRemoved = 0; - + // First remove all existing app windows. i=0; while (i < NW) { WindowState w = (WindowState)mWindows.get(i); if (w.mAppToken != null) { WindowState win = (WindowState)mWindows.remove(i); - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Rebuild removing window: " + win); NW--; numRemoved++; @@ -9114,12 +9287,12 @@ public class WindowManagerService extends IWindowManager.Stub } i++; } - + // The wallpaper window(s) typically live at the bottom of the stack, // so skip them before adding app tokens. lastWallpaper++; i = lastWallpaper; - + // First add all of the exiting app tokens... these are no longer // in the main app list, but still have windows shown. We put them // in the back because now that the animation is over we no longer @@ -9128,20 +9301,20 @@ public class WindowManagerService extends IWindowManager.Stub for (int j=0; j<NT; j++) { i = reAddAppWindowsLocked(i, mExitingAppTokens.get(j)); } - + // And add in the still active app tokens in Z order. NT = mAppTokens.size(); for (int j=0; j<NT; j++) { i = reAddAppWindowsLocked(i, mAppTokens.get(j)); } - + i -= lastWallpaper; if (i != numRemoved) { - Log.w(TAG, "Rebuild removed " + numRemoved + Slog.w(TAG, "Rebuild removed " + numRemoved + " windows but added " + i); } } - + private final void assignLayersLocked() { int N = mWindows.size(); int curBaseLayer = 0; @@ -9170,7 +9343,7 @@ public class WindowManagerService extends IWindowManager.Stub } else if (w.mIsWallpaper) { w.mAnimLayer += mWallpaperAnimLayerAdjustment; } - if (DEBUG_LAYERS) Log.v(TAG, "Assign layer " + w + ": " + if (DEBUG_LAYERS) Slog.v(TAG, "Assign layer " + w + ": " + w.mAnimLayer); //System.out.println( // "Assigned layer " + curLayer + " to " + w.mClient.asBinder()); @@ -9183,21 +9356,28 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG) { throw new RuntimeException("Recursive call!"); } - Log.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout"); + Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout"); return; } + if (mWaitingForConfig) { + // Our configuration has changed (most likely rotation), but we + // don't yet have the complete configuration to report to + // applications. Don't do any window layout until we have it. + return; + } + boolean recoveringMemory = false; if (mForceRemoves != null) { recoveringMemory = true; // Wait a little it for things to settle down, and off we go. for (int i=0; i<mForceRemoves.size(); i++) { WindowState ws = mForceRemoves.get(i); - Log.i(TAG, "Force removing: " + ws); + Slog.i(TAG, "Force removing: " + ws); removeWindowInnerLocked(ws.mSession, ws); } mForceRemoves = null; - Log.w(TAG, "Due to memory failure, waiting a bit for next layout"); + Slog.w(TAG, "Due to memory failure, waiting a bit for next layout"); Object tmp = new Object(); synchronized (tmp) { try { @@ -9233,134 +9413,110 @@ public class WindowManagerService extends IWindowManager.Stub } } catch (RuntimeException e) { mInLayout = false; - Log.e(TAG, "Unhandled exception while layout out windows", e); + Slog.e(TAG, "Unhandled exception while layout out windows", e); } } - private final void performLayoutLockedInner() { + private final int performLayoutLockedInner() { + if (!mLayoutNeeded) { + return 0; + } + + mLayoutNeeded = false; + final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); final int N = mWindows.size(); - int repeats = 0; int i; - if (DEBUG_LAYOUT) Log.v(TAG, "performLayout: needed=" + if (DEBUG_LAYOUT) Slog.v(TAG, "performLayout: needed=" + mLayoutNeeded + " dw=" + dw + " dh=" + dh); - // FIRST LOOP: Perform a layout, if needed. - - while (mLayoutNeeded) { - mPolicy.beginLayoutLw(dw, dh); - - // First perform layout of any root windows (not attached - // to another window). - int topAttached = -1; - for (i = N-1; i >= 0; i--) { - WindowState win = (WindowState) mWindows.get(i); - - // Don't do layout of a window if it is not visible, or - // soon won't be visible, to avoid wasting time and funky - // changes while a window is animating away. - final AppWindowToken atoken = win.mAppToken; - final boolean gone = win.mViewVisibility == View.GONE - || !win.mRelayoutCalled - || win.mRootToken.hidden - || (atoken != null && atoken.hiddenRequested) - || win.mAttachedHidden - || win.mExiting || win.mDestroying; - - if (win.mLayoutAttached) { - if (DEBUG_LAYOUT) Log.v(TAG, "First pass " + win - + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame - + " mLayoutAttached=" + win.mLayoutAttached); - if (DEBUG_LAYOUT && gone) Log.v(TAG, " (mViewVisibility=" - + win.mViewVisibility + " mRelayoutCalled=" - + win.mRelayoutCalled + " hidden=" - + win.mRootToken.hidden + " hiddenRequested=" - + (atoken != null && atoken.hiddenRequested) - + " mAttachedHidden=" + win.mAttachedHidden); - } - - // If this view is GONE, then skip it -- keep the current - // frame, and let the caller know so they can ignore it - // if they want. (We do the normal layout for INVISIBLE - // windows, since that means "perform layout as normal, - // just don't display"). - if (!gone || !win.mHaveFrame) { - if (!win.mLayoutAttached) { - mPolicy.layoutWindowLw(win, win.mAttrs, null); - if (DEBUG_LAYOUT) Log.v(TAG, "-> mFrame=" - + win.mFrame + " mContainingFrame=" - + win.mContainingFrame + " mDisplayFrame=" - + win.mDisplayFrame); - } else { - if (topAttached < 0) topAttached = i; - } - } - } - - // Now perform layout of attached windows, which usually - // depend on the position of the window they are attached to. - // XXX does not deal with windows that are attached to windows - // that are themselves attached. - for (i = topAttached; i >= 0; i--) { - WindowState win = (WindowState) mWindows.get(i); + mPolicy.beginLayoutLw(dw, dh); - // If this view is GONE, then skip it -- keep the current - // frame, and let the caller know so they can ignore it - // if they want. (We do the normal layout for INVISIBLE - // windows, since that means "perform layout as normal, - // just don't display"). - if (win.mLayoutAttached) { - if (DEBUG_LAYOUT) Log.v(TAG, "Second pass " + win - + " mHaveFrame=" + win.mHaveFrame - + " mViewVisibility=" + win.mViewVisibility - + " mRelayoutCalled=" + win.mRelayoutCalled); - if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled) - || !win.mHaveFrame) { - mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); - if (DEBUG_LAYOUT) Log.v(TAG, "-> mFrame=" - + win.mFrame + " mContainingFrame=" - + win.mContainingFrame + " mDisplayFrame=" - + win.mDisplayFrame); - } - } + int seq = mLayoutSeq+1; + if (seq < 0) seq = 0; + mLayoutSeq = seq; + + // First perform layout of any root windows (not attached + // to another window). + int topAttached = -1; + for (i = N-1; i >= 0; i--) { + WindowState win = (WindowState) mWindows.get(i); + + // Don't do layout of a window if it is not visible, or + // soon won't be visible, to avoid wasting time and funky + // changes while a window is animating away. + final AppWindowToken atoken = win.mAppToken; + final boolean gone = win.mViewVisibility == View.GONE + || !win.mRelayoutCalled + || win.mRootToken.hidden + || (atoken != null && atoken.hiddenRequested) + || win.mAttachedHidden + || win.mExiting || win.mDestroying; + + if (!win.mLayoutAttached) { + if (DEBUG_LAYOUT) Slog.v(TAG, "First pass " + win + + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame + + " mLayoutAttached=" + win.mLayoutAttached); + if (DEBUG_LAYOUT && gone) Slog.v(TAG, " (mViewVisibility=" + + win.mViewVisibility + " mRelayoutCalled=" + + win.mRelayoutCalled + " hidden=" + + win.mRootToken.hidden + " hiddenRequested=" + + (atoken != null && atoken.hiddenRequested) + + " mAttachedHidden=" + win.mAttachedHidden); } - - int changes = mPolicy.finishLayoutLw(); - if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) { - if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) { - assignLayersLocked(); + + // If this view is GONE, then skip it -- keep the current + // frame, and let the caller know so they can ignore it + // if they want. (We do the normal layout for INVISIBLE + // windows, since that means "perform layout as normal, + // just don't display"). + if (!gone || !win.mHaveFrame) { + if (!win.mLayoutAttached) { + mPolicy.layoutWindowLw(win, win.mAttrs, null); + win.mLayoutSeq = seq; + if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame=" + + win.mFrame + " mContainingFrame=" + + win.mContainingFrame + " mDisplayFrame=" + + win.mDisplayFrame); + } else { + if (topAttached < 0) topAttached = i; } } - if (changes == 0) { - mLayoutNeeded = false; - } else if (repeats > 2) { - Log.w(TAG, "Layout repeat aborted after too many iterations"); - mLayoutNeeded = false; - if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) { - Configuration newConfig = updateOrientationFromAppTokensLocked( - null, null); - if (newConfig != null) { - mLayoutNeeded = true; - mH.sendEmptyMessage(H.COMPUTE_AND_SEND_NEW_CONFIGURATION); - } - } - } else { - if (DEBUG_LAYOUT) Log.v(TAG, "Repeating layout because changes=0x" - + Integer.toHexString(changes)); - repeats++; - if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) { - if (DEBUG_LAYOUT) Log.v(TAG, "Computing new config from layout"); - Configuration newConfig = updateOrientationFromAppTokensLocked( - null, null); - if (newConfig != null) { - mH.sendEmptyMessage(H.COMPUTE_AND_SEND_NEW_CONFIGURATION); - } + } + + // Now perform layout of attached windows, which usually + // depend on the position of the window they are attached to. + // XXX does not deal with windows that are attached to windows + // that are themselves attached. + for (i = topAttached; i >= 0; i--) { + WindowState win = (WindowState) mWindows.get(i); + + // If this view is GONE, then skip it -- keep the current + // frame, and let the caller know so they can ignore it + // if they want. (We do the normal layout for INVISIBLE + // windows, since that means "perform layout as normal, + // just don't display"). + if (win.mLayoutAttached) { + if (DEBUG_LAYOUT) Slog.v(TAG, "Second pass " + win + + " mHaveFrame=" + win.mHaveFrame + + " mViewVisibility=" + win.mViewVisibility + + " mRelayoutCalled=" + win.mRelayoutCalled); + if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled) + || !win.mHaveFrame) { + mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); + win.mLayoutSeq = seq; + if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame=" + + win.mFrame + " mContainingFrame=" + + win.mContainingFrame + " mDisplayFrame=" + + win.mDisplayFrame); } } } + + return mPolicy.finishLayoutLw(); } private final void performLayoutAndPlaceSurfacesLockedInner( @@ -9371,14 +9527,16 @@ public class WindowManagerService extends IWindowManager.Stub int i; - // FIRST LOOP: Perform a layout, if needed. - performLayoutLockedInner(); - + if (mFocusMayChange) { + mFocusMayChange = false; + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); + } + if (mFxSession == null) { mFxSession = new SurfaceSession(); } - if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION"); + if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION"); // Initialize state of exiting tokens. for (i=mExitingTokens.size()-1; i>=0; i--) { @@ -9390,19 +9548,59 @@ public class WindowManagerService extends IWindowManager.Stub mExitingAppTokens.get(i).hasVisible = false; } - // SECOND LOOP: Execute animations and update visibility of windows. boolean orientationChangeComplete = true; Session holdScreen = null; float screenBrightness = -1; + float buttonBrightness = -1; boolean focusDisplayed = false; boolean animating = false; Surface.openTransaction(); try { - boolean restart; - boolean forceHiding = false; - + boolean wallpaperForceHidingChanged = false; + int repeats = 0; + int changes = 0; + do { + repeats++; + if (repeats > 6) { + Slog.w(TAG, "Animation repeat aborted after too many iterations"); + mLayoutNeeded = false; + break; + } + + if ((changes&(WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER + | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG + | WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT)) != 0) { + if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) { + if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) { + assignLayersLocked(); + mLayoutNeeded = true; + } + } + if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) { + if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout"); + if (updateOrientationFromAppTokensLocked()) { + mLayoutNeeded = true; + mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } + } + if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) { + mLayoutNeeded = true; + } + } + + // FIRST LOOP: Perform a layout, if needed. + if (repeats < 4) { + changes = performLayoutLockedInner(); + if (changes != 0) { + continue; + } + } else { + Slog.w(TAG, "Layout repeat skipped after too many iterations"); + changes = 0; + } + final int transactionSequence = ++mTransactionSequence; // Update animations of all applications, including those @@ -9421,18 +9619,22 @@ public class WindowManagerService extends IWindowManager.Stub } } + // SECOND LOOP: Execute animations and update visibility of windows. + + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq=" + + transactionSequence + " tokensAnimating=" + + tokensAnimating); + animating = tokensAnimating; - restart = false; boolean tokenMayBeDrawn = false; boolean wallpaperMayChange = false; - boolean focusMayChange = false; - boolean wallpaperForceHidingChanged = false; + boolean forceHiding = false; mPolicy.beginAnimationLw(dw, dh); final int N = mWindows.size(); - + for (i=N-1; i>=0; i--) { WindowState w = (WindowState)mWindows.get(i); @@ -9443,12 +9645,12 @@ public class WindowManagerService extends IWindowManager.Stub if (w.commitFinishDrawingLocked(currentTime)) { if ((w.mAttrs.flags & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) { - if (DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_WALLPAPER) Slog.v(TAG, "First draw done in potential wallpaper target " + w); wallpaperMayChange = true; } } - + boolean wasAnimating = w.mAnimating; if (w.stepAnimationLocked(currentTime, dw, dh)) { animating = true; @@ -9457,11 +9659,14 @@ public class WindowManagerService extends IWindowManager.Stub if (wasAnimating && !w.mAnimating && mWallpaperTarget == w) { wallpaperMayChange = true; } - + if (mPolicy.doesForceHide(w, attrs)) { if (!wasAnimating && animating) { + if (DEBUG_VISIBILITY) Slog.v(TAG, + "Animation done that could impact force hide: " + + w); wallpaperForceHidingChanged = true; - focusMayChange = true; + mFocusMayChange = true; } else if (w.isReadyForDisplay() && w.mAnimation == null) { forceHiding = true; } @@ -9469,17 +9674,30 @@ public class WindowManagerService extends IWindowManager.Stub boolean changed; if (forceHiding) { changed = w.hideLw(false, false); + if (DEBUG_VISIBILITY && changed) Slog.v(TAG, + "Now policy hidden: " + w); } else { changed = w.showLw(false, false); - if (changed && wallpaperForceHidingChanged - && w.isReadyForDisplay()) { - // Assume we will need to animate. If - // we don't (because the wallpaper will - // stay with the lock screen), then we will - // clean up later. - Animation a = mPolicy.createForceHideEnterAnimation(); - if (a != null) { - w.setAnimation(a); + if (DEBUG_VISIBILITY && changed) Slog.v(TAG, + "Now policy shown: " + w); + if (changed) { + if (wallpaperForceHidingChanged + && w.isVisibleNow() /*w.isReadyForDisplay()*/) { + // Assume we will need to animate. If + // we don't (because the wallpaper will + // stay with the lock screen), then we will + // clean up later. + Animation a = mPolicy.createForceHideEnterAnimation(); + if (a != null) { + w.setAnimation(a); + } + } + if (mCurrentFocus == null || + mCurrentFocus.mLayer < w.mLayer) { + // We are showing on to of the current + // focus, so re-evaluate focus to make + // sure it is correct. + mFocusMayChange = true; } } } @@ -9487,17 +9705,8 @@ public class WindowManagerService extends IWindowManager.Stub & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) { wallpaperMayChange = true; } - if (changed && !forceHiding - && (mCurrentFocus == null) - && (mFocusedApp != null)) { - // It's possible that the last focus recalculation left no - // current focused window even though the app has come to the - // foreground already. In this case, we make sure to recalculate - // focus when we show a window. - focusMayChange = true; - } } - + mPolicy.animatingWindowLw(w, attrs); } @@ -9512,11 +9721,11 @@ public class WindowManagerService extends IWindowManager.Stub == WindowManager.LayoutParams.TYPE_BASE_APPLICATION) && !w.mExiting && !w.mDestroying) { if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) { - Log.v(TAG, "Eval win " + w + ": isDrawn=" + Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw() + ", isAnimating=" + w.isAnimating()); if (!w.isDrawnLw()) { - Log.v(TAG, "Not displayed: s=" + w.mSurface + Slog.v(TAG, "Not displayed: s=" + w.mSurface + " pv=" + w.mPolicyVisibility + " dp=" + w.mDrawPending + " cdp=" + w.mCommitDrawPending @@ -9530,7 +9739,7 @@ public class WindowManagerService extends IWindowManager.Stub atoken.numInterestingWindows++; if (w.isDrawnLw()) { atoken.numDrawnWindows++; - if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG, "tokenMayBeDrawn: " + atoken + " freezingScreen=" + atoken.freezingScreen + " mAppFreezing=" + w.mAppFreezing); @@ -9546,9 +9755,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (mPolicy.finishAnimationLw()) { - restart = true; - } + changes |= mPolicy.finishAnimationLw(); if (tokenMayBeDrawn) { // See if any windows have been drawn, so they (and others @@ -9562,7 +9769,7 @@ public class WindowManagerService extends IWindowManager.Stub if (wtoken.freezingScreen) { int numInteresting = wtoken.numInterestingWindows; if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) { - if (DEBUG_VISIBILITY) Log.v(TAG, + if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawn: " + wtoken + " interesting=" + numInteresting + " drawn=" + wtoken.numDrawnWindows); @@ -9573,12 +9780,12 @@ public class WindowManagerService extends IWindowManager.Stub } else if (!wtoken.allDrawn) { int numInteresting = wtoken.numInterestingWindows; if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) { - if (DEBUG_VISIBILITY) Log.v(TAG, + if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawn: " + wtoken + " interesting=" + numInteresting + " drawn=" + wtoken.numDrawnWindows); wtoken.allDrawn = true; - restart = true; + changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM; // We can now show all of the drawn windows! if (!mOpeningApps.contains(wtoken)) { @@ -9595,7 +9802,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mAppTransitionReady) { int NN = mOpeningApps.size(); boolean goodToGo = true; - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Checking " + NN + " opening apps (frozen=" + mDisplayFrozen + " timeout=" + mAppTransitionTimeout + ")..."); @@ -9605,7 +9812,7 @@ public class WindowManagerService extends IWindowManager.Stub // we'll unfreeze the display when everyone is ready. for (i=0; i<NN && goodToGo; i++) { AppWindowToken wtoken = mOpeningApps.get(i); - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Check opening app" + wtoken + ": allDrawn=" + wtoken.allDrawn + " startingDisplayed=" + wtoken.startingDisplayed); @@ -9616,7 +9823,7 @@ public class WindowManagerService extends IWindowManager.Stub } } if (goodToGo) { - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "**** GOOD TO GO"); + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); int transit = mNextAppTransition; if (mSkipAppTransitionAnimation) { transit = WindowManagerPolicy.TRANSIT_UNSET; @@ -9646,19 +9853,19 @@ public class WindowManagerService extends IWindowManager.Stub } mToTopApps.clear(); } - + WindowState oldWallpaper = mWallpaperTarget; - + adjustWallpaperWindowsLocked(); wallpaperMayChange = false; - + // The top-most window will supply the layout params, // and we will determine it below. LayoutParams animLp = null; AppWindowToken animToken = null; int bestAnimLayer = -1; - - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, + + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New wallpaper target=" + mWallpaperTarget + ", lower target=" + mLowerWallpaperTarget + ", upper target=" + mUpperWallpaperTarget); @@ -9708,9 +9915,9 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + if (foundWallpapers == 3) { - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); switch (transit) { case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: @@ -9724,22 +9931,22 @@ public class WindowManagerService extends IWindowManager.Stub transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE; break; } - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit: " + transit); } else if (oldWallpaper != null) { // We are transitioning from an activity with // a wallpaper to one without. transit = WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE; - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: " + transit); } else if (mWallpaperTarget != null) { // We are transitioning from an activity without // a wallpaper to now showing the wallpaper transit = WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN; - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: " + transit); } - + if ((transit&WindowManagerPolicy.TRANSIT_ENTER_MASK) != 0) { mLastEnterAnimToken = animToken; mLastEnterAnimParams = animLp; @@ -9748,11 +9955,19 @@ public class WindowManagerService extends IWindowManager.Stub mLastEnterAnimToken = null; mLastEnterAnimParams = null; } + + // If all closing windows are obscured, then there is + // no need to do an animation. This is the case, for + // example, when this transition is being done behind + // the lock screen. + if (!mPolicy.allowAppAnimationsLw()) { + animLp = null; + } NN = mOpeningApps.size(); for (i=0; i<NN; i++) { AppWindowToken wtoken = mOpeningApps.get(i); - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); wtoken.reportedVisible = false; wtoken.inPendingTransaction = false; @@ -9765,7 +9980,7 @@ public class WindowManagerService extends IWindowManager.Stub NN = mClosingApps.size(); for (i=0; i<NN; i++) { AppWindowToken wtoken = mClosingApps.get(i); - if (DEBUG_APP_TRANSITIONS) Log.v(TAG, + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app" + wtoken); wtoken.inPendingTransaction = false; wtoken.animation = null; @@ -9779,24 +9994,24 @@ public class WindowManagerService extends IWindowManager.Stub } mNextAppTransitionPackage = null; - + mOpeningApps.clear(); mClosingApps.clear(); // This has changed the visibility of windows, so perform // a new layout to get them all up-to-date. + changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT; mLayoutNeeded = true; if (!moveInputMethodWindowsIfNeededLocked(true)) { assignLayersLocked(); } - performLayoutLockedInner(); updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES); - focusMayChange = false; - - restart = true; + mFocusMayChange = false; } } - + + int adjResult = 0; + if (!animating && mAppTransitionRunning) { // We have finished the animation of an app transition. To do // this, we have delayed a lot of operations like showing and @@ -9807,21 +10022,19 @@ public class WindowManagerService extends IWindowManager.Stub mAppTransitionRunning = false; // Clear information about apps that were moving. mToBottomApps.clear(); - + rebuildAppWindowListLocked(); - restart = true; + changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT; + adjResult |= ADJUST_WALLPAPER_LAYERS_CHANGED; moveInputMethodWindowsIfNeededLocked(false); wallpaperMayChange = true; - mLayoutNeeded = true; // Since the window list has been rebuilt, focus might // have to be recomputed since the actual order of windows // might have changed again. - focusMayChange = true; + mFocusMayChange = true; } - - int adjResult = 0; - - if (wallpaperForceHidingChanged) { + + if (wallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) { // At this point, there was a window with a wallpaper that // was force hiding other windows behind it, but now it // is going away. This may be simple -- just animate @@ -9829,10 +10042,28 @@ public class WindowManagerService extends IWindowManager.Stub // hard -- the wallpaper now needs to be shown behind // something that was hidden. WindowState oldWallpaper = mWallpaperTarget; - adjResult = adjustWallpaperWindowsLocked(); + if (mLowerWallpaperTarget != null + && mLowerWallpaperTarget.mAppToken != null) { + if (DEBUG_WALLPAPER) Slog.v(TAG, + "wallpaperForceHiding changed with lower=" + + mLowerWallpaperTarget); + if (DEBUG_WALLPAPER) Slog.v(TAG, + "hidden=" + mLowerWallpaperTarget.mAppToken.hidden + + " hiddenRequested=" + mLowerWallpaperTarget.mAppToken.hiddenRequested); + if (mLowerWallpaperTarget.mAppToken.hidden) { + // The lower target has become hidden before we + // actually started the animation... let's completely + // re-evaluate everything. + mLowerWallpaperTarget = mUpperWallpaperTarget = null; + changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM; + } + } + adjResult |= adjustWallpaperWindowsLocked(); wallpaperMayChange = false; - if (false) Log.v(TAG, "****** OLD: " + oldWallpaper - + " NEW: " + mWallpaperTarget); + wallpaperForceHidingChanged = false; + if (DEBUG_WALLPAPER) Slog.v(TAG, "****** OLD: " + oldWallpaper + + " NEW: " + mWallpaperTarget + + " LOWER: " + mLowerWallpaperTarget); if (mLowerWallpaperTarget == null) { // Whoops, we don't need a special wallpaper animation. // Clear them out. @@ -9842,7 +10073,7 @@ public class WindowManagerService extends IWindowManager.Stub if (w.mSurface != null) { final WindowManager.LayoutParams attrs = w.mAttrs; if (mPolicy.doesForceHide(w, attrs) && w.isVisibleLw()) { - if (DEBUG_FOCUS) Log.i(TAG, "win=" + w + " force hides other windows"); + if (DEBUG_FOCUS) Slog.i(TAG, "win=" + w + " force hides other windows"); forceHiding = true; } else if (mPolicy.canBeForceHidden(w, attrs)) { if (!w.mAnimating) { @@ -9855,39 +10086,40 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + if (wallpaperMayChange) { - if (DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper may change! Adjusting"); - adjResult = adjustWallpaperWindowsLocked(); + adjResult |= adjustWallpaperWindowsLocked(); } - + if ((adjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) { - if (DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper layer changed: assigning layers + relayout"); - restart = true; - mLayoutNeeded = true; + changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT; assignLayersLocked(); } else if ((adjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) { - if (DEBUG_WALLPAPER) Log.v(TAG, + if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility changed: relayout"); - restart = true; - mLayoutNeeded = true; + changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT; } - - if (focusMayChange) { + + if (mFocusMayChange) { + mFocusMayChange = false; if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES)) { - restart = true; + changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM; adjResult = 0; } } if (mLayoutNeeded) { - restart = true; - performLayoutLockedInner(); + changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT; } + + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x" + + Integer.toHexString(changes)); - } while (restart); + } while (changes != 0); // THIRD LOOP: Update the surfaces of all windows. @@ -9901,7 +10133,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean backgroundFillerShown = false; final int N = mWindows.size(); - + for (i=N-1; i>=0; i--) { WindowState w = (WindowState)mWindows.get(i); @@ -9910,8 +10142,22 @@ public class WindowManagerService extends IWindowManager.Stub final int attrFlags = attrs.flags; if (w.mSurface != null) { + // XXX NOTE: The logic here could be improved. We have + // the decision about whether to resize a window separated + // from whether to hide the surface. This can cause us to + // resize a surface even if we are going to hide it. You + // can see this by (1) holding device in landscape mode on + // home screen; (2) tapping browser icon (device will rotate + // to landscape; (3) tap home. The wallpaper will be resized + // in step 2 but then immediately hidden, causing us to + // have to resize and then redraw it again in step 3. It + // would be nice to figure out how to avoid this, but it is + // difficult because we do need to resize surfaces in some + // cases while they are hidden such as when first showing a + // window. + w.computeShownFrameLocked(); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Placing surface #" + i + " " + w.mSurface + ": new=" + w.mShownFrame + ", old=" + w.mLastShownFrame); @@ -9929,13 +10175,14 @@ public class WindowManagerService extends IWindowManager.Stub w.mLastRequestedHeight = height; w.mLastShownFrame.set(w.mShownFrame); try { - if (SHOW_TRANSACTIONS) Log.i( - TAG, " SURFACE " + w.mSurface - + ": POS " + w.mShownFrame.left - + ", " + w.mShownFrame.top); + if (SHOW_TRANSACTIONS) logSurface(w, + "POS " + w.mShownFrame.left + + ", " + w.mShownFrame.top, null); + w.mSurfaceX = w.mShownFrame.left; + w.mSurfaceY = w.mShownFrame.top; w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top); } catch (RuntimeException e) { - Log.w(TAG, "Error positioning surface in " + w, e); + Slog.w(TAG, "Error positioning surface in " + w, e); if (!recoveringMemory) { reclaimSomeSurfaceMemoryLocked(w, "position"); } @@ -9952,20 +10199,24 @@ public class WindowManagerService extends IWindowManager.Stub if (height < 1) height = 1; if (w.mSurface != null) { try { - if (SHOW_TRANSACTIONS) Log.i( - TAG, " SURFACE " + w.mSurface + ": POS " - + w.mShownFrame.left + "," + if (SHOW_TRANSACTIONS) logSurface(w, + "POS " + w.mShownFrame.left + "," + w.mShownFrame.top + " SIZE " + w.mShownFrame.width() + "x" - + w.mShownFrame.height()); + + w.mShownFrame.height(), null); + w.mSurfaceResized = true; + w.mSurfaceW = width; + w.mSurfaceH = height; w.mSurface.setSize(width, height); + w.mSurfaceX = w.mShownFrame.left; + w.mSurfaceY = w.mShownFrame.top; w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top); } catch (RuntimeException e) { // If something goes wrong with the surface (such // as running out of memory), don't take down the // entire system. - Log.e(TAG, "Failure updating surface of " + w + Slog.e(TAG, "Failure updating surface of " + w + "size=(" + width + "x" + height + "), pos=(" + w.mShownFrame.left + "," + w.mShownFrame.top + ")", e); @@ -9975,14 +10226,27 @@ public class WindowManagerService extends IWindowManager.Stub } } } - if (!w.mAppFreezing) { + if (!w.mAppFreezing && w.mLayoutSeq == mLayoutSeq) { w.mContentInsetsChanged = !w.mLastContentInsets.equals(w.mContentInsets); w.mVisibleInsetsChanged = !w.mLastVisibleInsets.equals(w.mVisibleInsets); + boolean configChanged = + w.mConfiguration != mCurConfiguration + && (w.mConfiguration == null + || mCurConfiguration.diff(w.mConfiguration) != 0); + if (DEBUG_CONFIGURATION && configChanged) { + Slog.v(TAG, "Win " + w + " config changed: " + + mCurConfiguration); + } + if (localLOGV) Slog.v(TAG, "Resizing " + w + + ": configChanged=" + configChanged + + " last=" + w.mLastFrame + " frame=" + w.mFrame); if (!w.mLastFrame.equals(w.mFrame) || w.mContentInsetsChanged - || w.mVisibleInsetsChanged) { + || w.mVisibleInsetsChanged + || w.mSurfaceResized + || configChanged) { w.mLastFrame.set(w.mFrame); w.mLastContentInsets.set(w.mContentInsets); w.mLastVisibleInsets.set(w.mVisibleInsets); @@ -9990,10 +10254,10 @@ public class WindowManagerService extends IWindowManager.Stub // it frozen until this window draws at its new // orientation. if (mDisplayFrozen) { - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Slog.v(TAG, "Resizing while display frozen: " + w); w.mOrientationChanging = true; - if (mWindowsFreezingScreen) { + if (!mWindowsFreezingScreen) { mWindowsFreezingScreen = true; // XXX should probably keep timeout from // when we first froze the display. @@ -10008,7 +10272,7 @@ public class WindowManagerService extends IWindowManager.Stub // to go through the process of getting informed // by the application when it has finished drawing. if (w.mOrientationChanging) { - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation start waiting for draw in " + w + ", surface " + w.mSurface); w.mDrawPending = true; @@ -10018,12 +10282,12 @@ public class WindowManagerService extends IWindowManager.Stub w.mAppToken.allDrawn = false; } } - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Resizing window " + w + " to " + w.mFrame); mResizingWindows.add(w); } else if (w.mOrientationChanging) { if (!w.mDrawPending && !w.mCommitDrawPending) { - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation not waiting for draw in " + w + ", surface " + w.mSurface); w.mOrientationChanging = false; @@ -10034,14 +10298,21 @@ public class WindowManagerService extends IWindowManager.Stub if (w.mAttachedHidden || !w.isReadyForDisplay()) { if (!w.mLastHidden) { //dump(); + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Window hiding: waitingToShow=" + + w.mRootToken.waitingToShow + " polvis=" + + w.mPolicyVisibility + " atthid=" + + w.mAttachedHidden + " tokhid=" + + w.mRootToken.hidden + " vis=" + + w.mViewVisibility); w.mLastHidden = true; - if (SHOW_TRANSACTIONS) Log.i( - TAG, " SURFACE " + w.mSurface + ": HIDE (performLayout)"); + if (SHOW_TRANSACTIONS) logSurface(w, + "HIDE (performLayout)", null); if (w.mSurface != null) { + w.mSurfaceShown = false; try { w.mSurface.hide(); } catch (RuntimeException e) { - Log.w(TAG, "Exception hiding surface in " + w); + Slog.w(TAG, "Exception hiding surface in " + w); } } mKeyWaiter.releasePendingPointerLocked(w.mSession); @@ -10054,7 +10325,7 @@ public class WindowManagerService extends IWindowManager.Stub // new orientation. if (w.mOrientationChanging) { w.mOrientationChanging = false; - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change skips hidden " + w); } } else if (w.mLastLayer != w.mAnimLayer @@ -10075,22 +10346,23 @@ public class WindowManagerService extends IWindowManager.Stub w.mLastDtDy = w.mDtDy; w.mLastHScale = w.mHScale; w.mLastVScale = w.mVScale; - if (SHOW_TRANSACTIONS) Log.i( - TAG, " SURFACE " + w.mSurface + ": alpha=" - + w.mShownAlpha + " layer=" + w.mAnimLayer + if (SHOW_TRANSACTIONS) logSurface(w, + "alpha=" + w.mShownAlpha + " layer=" + w.mAnimLayer + " matrix=[" + (w.mDsDx*w.mHScale) + "," + (w.mDtDx*w.mVScale) + "][" + (w.mDsDy*w.mHScale) - + "," + (w.mDtDy*w.mVScale) + "]"); + + "," + (w.mDtDy*w.mVScale) + "]", null); if (w.mSurface != null) { try { + w.mSurfaceAlpha = w.mShownAlpha; w.mSurface.setAlpha(w.mShownAlpha); + w.mSurfaceLayer = w.mAnimLayer; w.mSurface.setLayer(w.mAnimLayer); w.mSurface.setMatrix( w.mDsDx*w.mHScale, w.mDtDx*w.mVScale, w.mDsDy*w.mHScale, w.mDtDy*w.mVScale); } catch (RuntimeException e) { - Log.w(TAG, "Error updating surface in " + w, e); + Slog.w(TAG, "Error updating surface in " + w, e); if (!recoveringMemory) { reclaimSomeSurfaceMemoryLocked(w, "update"); } @@ -10100,9 +10372,9 @@ public class WindowManagerService extends IWindowManager.Stub if (w.mLastHidden && !w.mDrawPending && !w.mCommitDrawPending && !w.mReadyToShow) { - if (SHOW_TRANSACTIONS) Log.i( - TAG, " SURFACE " + w.mSurface + ": SHOW (performLayout)"); - if (DEBUG_VISIBILITY) Log.v(TAG, "Showing " + w + if (SHOW_TRANSACTIONS) logSurface(w, + "SHOW (performLayout)", null); + if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w + " during relayout"); if (showSurfaceRobustlyLocked(w)) { w.mHasDrawn = true; @@ -10120,26 +10392,26 @@ public class WindowManagerService extends IWindowManager.Stub if (displayed) { if (!covered) { - if (attrs.width == LayoutParams.FILL_PARENT - && attrs.height == LayoutParams.FILL_PARENT) { + if (attrs.width == LayoutParams.MATCH_PARENT + && attrs.height == LayoutParams.MATCH_PARENT) { covered = true; } } if (w.mOrientationChanging) { if (w.mDrawPending || w.mCommitDrawPending) { orientationChangeComplete = false; - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation continue waiting for draw in " + w); } else { w.mOrientationChanging = false; - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w); } } w.mToken.hasVisible = true; } } else if (w.mOrientationChanging) { - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change skips hidden " + w); w.mOrientationChanging = false; } @@ -10151,7 +10423,7 @@ public class WindowManagerService extends IWindowManager.Stub } final boolean obscuredChanged = w.mObscured != obscured; - + // Update effect. if (!(w.mObscured=obscured)) { if (w.mSurface != null) { @@ -10162,9 +10434,14 @@ public class WindowManagerService extends IWindowManager.Stub && screenBrightness < 0) { screenBrightness = w.mAttrs.screenBrightness; } - if (attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG - || attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD - || attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_ERROR) { + if (!syswin && w.mAttrs.buttonBrightness >= 0 + && buttonBrightness < 0) { + buttonBrightness = w.mAttrs.buttonBrightness; + } + if (canBeSeen + && (attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG + || attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD + || attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_ERROR)) { syswin = true; } } @@ -10176,17 +10453,18 @@ public class WindowManagerService extends IWindowManager.Stub // performance reasons). obscured = true; } else if (opaqueDrawn && w.needsBackgroundFiller(dw, dh)) { - if (SHOW_TRANSACTIONS) Log.d(TAG, "showing background filler"); + if (SHOW_TRANSACTIONS) Slog.d(TAG, "showing background filler"); // This window is in compatibility mode, and needs background filler. obscured = true; if (mBackgroundFillerSurface == null) { try { mBackgroundFillerSurface = new Surface(mFxSession, 0, + "BackGroundFiller", 0, dw, dh, PixelFormat.OPAQUE, Surface.FX_SURFACE_NORMAL); } catch (Exception e) { - Log.e(TAG, "Exception creating filler surface", e); + Slog.e(TAG, "Exception creating filler surface", e); } } try { @@ -10197,62 +10475,67 @@ public class WindowManagerService extends IWindowManager.Stub mBackgroundFillerSurface.setLayer(w.mAnimLayer - 1); mBackgroundFillerSurface.show(); } catch (RuntimeException e) { - Log.e(TAG, "Exception showing filler surface"); + Slog.e(TAG, "Exception showing filler surface"); } backgroundFillerShown = true; mBackgroundFillerShown = true; } else if (canBeSeen && !obscured && (attrFlags&FLAG_BLUR_BEHIND|FLAG_DIM_BEHIND) != 0) { - if (localLOGV) Log.v(TAG, "Win " + w + if (localLOGV) Slog.v(TAG, "Win " + w + ": blurring=" + blurring + " obscured=" + obscured + " displayed=" + displayed); if ((attrFlags&FLAG_DIM_BEHIND) != 0) { if (!dimming) { - //Log.i(TAG, "DIM BEHIND: " + w); + //Slog.i(TAG, "DIM BEHIND: " + w); dimming = true; if (mDimAnimator == null) { mDimAnimator = new DimAnimator(mFxSession); } mDimAnimator.show(dw, dh); + mDimAnimator.updateParameters(w, currentTime); } - mDimAnimator.updateParameters(w, currentTime); } if ((attrFlags&FLAG_BLUR_BEHIND) != 0) { if (!blurring) { - //Log.i(TAG, "BLUR BEHIND: " + w); + //Slog.i(TAG, "BLUR BEHIND: " + w); blurring = true; - mBlurShown = true; if (mBlurSurface == null) { - if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " + if (SHOW_TRANSACTIONS) Slog.i(TAG, " BLUR " + mBlurSurface + ": CREATE"); try { mBlurSurface = new Surface(mFxSession, 0, + "BlurSurface", -1, 16, 16, PixelFormat.OPAQUE, Surface.FX_SURFACE_BLUR); } catch (Exception e) { - Log.e(TAG, "Exception creating Blur surface", e); + Slog.e(TAG, "Exception creating Blur surface", e); } } - if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " - + mBlurSurface + ": SHOW pos=(0,0) (" + - dw + "x" + dh + "), layer=" + (w.mAnimLayer-1)); if (mBlurSurface != null) { + if (SHOW_TRANSACTIONS) Slog.i(TAG, " BLUR " + + mBlurSurface + ": pos=(0,0) (" + + dw + "x" + dh + "), layer=" + (w.mAnimLayer-1)); mBlurSurface.setPosition(0, 0); mBlurSurface.setSize(dw, dh); - try { - mBlurSurface.show(); - } catch (RuntimeException e) { - Log.w(TAG, "Failure showing blur surface", e); + mBlurSurface.setLayer(w.mAnimLayer-2); + if (!mBlurShown) { + try { + if (SHOW_TRANSACTIONS) Slog.i(TAG, " BLUR " + + mBlurSurface + ": SHOW"); + mBlurSurface.show(); + } catch (RuntimeException e) { + Slog.w(TAG, "Failure showing blur surface", e); + } + mBlurShown = true; } } } - mBlurSurface.setLayer(w.mAnimLayer-2); } } } - + if (obscuredChanged && mWallpaperTarget == w) { // This is the wallpaper target and its obscured state // changed... make sure the current wallaper's visibility @@ -10260,40 +10543,41 @@ public class WindowManagerService extends IWindowManager.Stub updateWallpaperVisibilityLocked(); } } - + if (backgroundFillerShown == false && mBackgroundFillerShown) { mBackgroundFillerShown = false; - if (SHOW_TRANSACTIONS) Log.d(TAG, "hiding background filler"); + if (SHOW_TRANSACTIONS) Slog.d(TAG, "hiding background filler"); try { mBackgroundFillerSurface.hide(); } catch (RuntimeException e) { - Log.e(TAG, "Exception hiding filler surface", e); + Slog.e(TAG, "Exception hiding filler surface", e); } } if (mDimAnimator != null && mDimAnimator.mDimShown) { - animating |= mDimAnimator.updateSurface(dimming, currentTime, mDisplayFrozen); + animating |= mDimAnimator.updateSurface(dimming, currentTime, + mDisplayFrozen || !mPolicy.isScreenOn()); } if (!blurring && mBlurShown) { - if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " + mBlurSurface + if (SHOW_TRANSACTIONS) Slog.i(TAG, " BLUR " + mBlurSurface + ": HIDE"); try { mBlurSurface.hide(); } catch (IllegalArgumentException e) { - Log.w(TAG, "Illegal argument exception hiding blur surface"); + Slog.w(TAG, "Illegal argument exception hiding blur surface"); } mBlurShown = false; } - if (SHOW_TRANSACTIONS) Log.i(TAG, "<<< CLOSE TRANSACTION"); + if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION"); } catch (RuntimeException e) { - Log.e(TAG, "Unhandled exception in Window Manager", e); + Slog.e(TAG, "Unhandled exception in Window Manager", e); } Surface.closeTransaction(); - if (DEBUG_ORIENTATION && mDisplayFrozen) Log.v(TAG, + if (DEBUG_ORIENTATION && mDisplayFrozen) Slog.v(TAG, "With display frozen, orientationChangeComplete=" + orientationChangeComplete); if (orientationChangeComplete) { @@ -10301,9 +10585,7 @@ public class WindowManagerService extends IWindowManager.Stub mWindowsFreezingScreen = false; mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); } - if (mAppsFreezingScreen == 0) { - stopFreezingDisplayLocked(); - } + stopFreezingDisplayLocked(); } i = mResizingWindows.size(); @@ -10312,13 +10594,28 @@ public class WindowManagerService extends IWindowManager.Stub i--; WindowState win = mResizingWindows.get(i); try { - if (DEBUG_ORIENTATION) Log.v(TAG, "Reporting new frame to " - + win + ": " + win.mFrame); + if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, + "Reporting new frame to " + win + ": " + win.mFrame); + int diff = 0; + boolean configChanged = + win.mConfiguration != mCurConfiguration + && (win.mConfiguration == null + || (diff=mCurConfiguration.diff(win.mConfiguration)) != 0); + if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) + && configChanged) { + Slog.i(TAG, "Sending new config to window " + win + ": " + + win.mFrame.width() + "x" + win.mFrame.height() + + " / " + mCurConfiguration + " / 0x" + + Integer.toHexString(diff)); + } + win.mConfiguration = mCurConfiguration; win.mClient.resized(win.mFrame.width(), win.mFrame.height(), win.mLastContentInsets, - win.mLastVisibleInsets, win.mDrawPending); + win.mLastVisibleInsets, win.mDrawPending, + configChanged ? win.mConfiguration : null); win.mContentInsetsChanged = false; win.mVisibleInsetsChanged = false; + win.mSurfaceResized = false; } catch (RemoteException e) { win.mOrientationChanging = false; } @@ -10375,7 +10672,7 @@ public class WindowManagerService extends IWindowManager.Stub } boolean needRelayout = false; - + if (!animating && mAppTransitionRunning) { // We have finished the animation of an app transition. To do // this, we have delayed a lot of operations like showing and @@ -10386,10 +10683,11 @@ public class WindowManagerService extends IWindowManager.Stub mAppTransitionRunning = false; needRelayout = true; rebuildAppWindowListLocked(); + assignLayersLocked(); // Clear information about apps that were moving. mToBottomApps.clear(); } - + if (focusDisplayed) { mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS); } @@ -10401,20 +10699,32 @@ public class WindowManagerService extends IWindowManager.Stub } else if (animating) { 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); - mH.sendMessage(m); - } + if (DEBUG_FREEZE) Slog.v(TAG, "Layout: mDisplayFrozen=" + mDisplayFrozen + + " holdScreen=" + holdScreen); + if (!mDisplayFrozen) { + mQueue.setHoldScreenLocked(holdScreen != null); + if (screenBrightness < 0 || screenBrightness > 1.0f) { + mPowerManager.setScreenBrightnessOverride(-1); + } else { + mPowerManager.setScreenBrightnessOverride((int) + (screenBrightness * Power.BRIGHTNESS_ON)); + } + if (buttonBrightness < 0 || buttonBrightness > 1.0f) { + mPowerManager.setButtonBrightnessOverride(-1); + } else { + mPowerManager.setButtonBrightnessOverride((int) + (buttonBrightness * Power.BRIGHTNESS_ON)); + } + if (holdScreen != mHoldingScreenOn) { + mHoldingScreenOn = holdScreen; + Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen); + mH.sendMessage(m); + } + } + if (mTurnOnScreen) { + if (DEBUG_VISIBILITY) Slog.v(TAG, "Turning screen on after layout!"); mPowerManager.userActivity(SystemClock.uptimeMillis(), false, LocalPowerManager.BUTTON_EVENT, true); mTurnOnScreen = false; @@ -10443,15 +10753,18 @@ public class WindowManagerService extends IWindowManager.Stub boolean showSurfaceRobustlyLocked(WindowState win) { try { if (win.mSurface != null) { + win.mSurfaceShown = true; win.mSurface.show(); if (win.mTurnOnScreen) { + if (DEBUG_VISIBILITY) Slog.v(TAG, + "Show surface turning screen on: " + win); win.mTurnOnScreen = false; mTurnOnScreen = true; } } return true; } catch (RuntimeException e) { - Log.w(TAG, "Failure showing surface " + win.mSurface + " in " + win); + Slog.w(TAG, "Failure showing surface " + win.mSurface + " in " + win); } reclaimSomeSurfaceMemoryLocked(win, "show"); @@ -10462,7 +10775,7 @@ public class WindowManagerService extends IWindowManager.Stub void reclaimSomeSurfaceMemoryLocked(WindowState win, String operation) { final Surface surface = win.mSurface; - EventLog.writeEvent(LOG_WM_NO_SURFACE_MEMORY, win.toString(), + EventLog.writeEvent(EventLogTags.WM_NO_SURFACE_MEMORY, win.toString(), win.mSession.mPid, operation); if (mForceRemoves == null) { @@ -10476,27 +10789,29 @@ public class WindowManagerService extends IWindowManager.Stub // around. int N = mWindows.size(); boolean leakedSurface = false; - Log.i(TAG, "Out of memory for surface! Looking for leaks..."); + Slog.i(TAG, "Out of memory for surface! Looking for leaks..."); for (int i=0; i<N; i++) { WindowState ws = (WindowState)mWindows.get(i); if (ws.mSurface != null) { if (!mSessions.contains(ws.mSession)) { - Log.w(TAG, "LEAKED SURFACE (session doesn't exist): " + Slog.w(TAG, "LEAKED SURFACE (session doesn't exist): " + ws + " surface=" + ws.mSurface + " token=" + win.mToken + " pid=" + ws.mSession.mPid + " uid=" + ws.mSession.mUid); ws.mSurface.destroy(); + ws.mSurfaceShown = false; ws.mSurface = null; mForceRemoves.add(ws); i--; N--; leakedSurface = true; } else if (win.mAppToken != null && win.mAppToken.clientHidden) { - Log.w(TAG, "LEAKED SURFACE (app token hidden): " + Slog.w(TAG, "LEAKED SURFACE (app token hidden): " + ws + " surface=" + ws.mSurface + " token=" + win.mAppToken); ws.mSurface.destroy(); + ws.mSurfaceShown = false; ws.mSurface = null; leakedSurface = true; } @@ -10505,7 +10820,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean killedApps = false; if (!leakedSurface) { - Log.w(TAG, "No leaked surfaces; killing applicatons!"); + Slog.w(TAG, "No leaked surfaces; killing applicatons!"); SparseIntArray pidCandidates = new SparseIntArray(); for (int i=0; i<N; i++) { WindowState ws = (WindowState)mWindows.get(i); @@ -10519,7 +10834,7 @@ public class WindowManagerService extends IWindowManager.Stub pids[i] = pidCandidates.keyAt(i); } try { - if (mActivityManager.killPidsForMemory(pids)) { + if (mActivityManager.killPids(pids, "Free memory")) { killedApps = true; } } catch (RemoteException e) { @@ -10530,9 +10845,10 @@ public class WindowManagerService extends IWindowManager.Stub if (leakedSurface || killedApps) { // We managed to reclaim some memory, so get rid of the trouble // surface and ask the app to request another one. - Log.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry."); + Slog.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry."); if (surface != null) { surface.destroy(); + win.mSurfaceShown = false; win.mSurface = null; } @@ -10553,7 +10869,7 @@ public class WindowManagerService extends IWindowManager.Stub // change message pending. mH.removeMessages(H.REPORT_FOCUS_CHANGE); mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus); final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; @@ -10595,7 +10911,7 @@ public class WindowManagerService extends IWindowManager.Stub while (i >= 0) { win = (WindowState)mWindows.get(i); - if (localLOGV || DEBUG_FOCUS) Log.v( + if (localLOGV || DEBUG_FOCUS) Slog.v( TAG, "Looking for focus: " + i + " = " + win + ", flags=" + win.mAttrs.flags @@ -10619,7 +10935,7 @@ public class WindowManagerService extends IWindowManager.Stub if (nextApp == mFocusedApp) { // Whoops, we are below the focused app... no focus // for you! - if (localLOGV || DEBUG_FOCUS) Log.v( + if (localLOGV || DEBUG_FOCUS) Slog.v( TAG, "Reached focused app: " + mFocusedApp); return null; } @@ -10640,7 +10956,7 @@ public class WindowManagerService extends IWindowManager.Stub // Dispatch to this window if it is wants key events. if (win.canReceiveKeys()) { - if (DEBUG_FOCUS) Log.v( + if (DEBUG_FOCUS) Slog.v( TAG, "Found focus @ " + i + " = " + win); result = win; break; @@ -10669,10 +10985,10 @@ public class WindowManagerService extends IWindowManager.Stub mScreenFrozenLock.acquire(); long now = SystemClock.uptimeMillis(); - //Log.i(TAG, "Freezing, gc pending: " + mFreezeGcPending + ", now " + now); + //Slog.i(TAG, "Freezing, gc pending: " + mFreezeGcPending + ", now " + now); if (mFreezeGcPending != 0) { if (now > (mFreezeGcPending+1000)) { - //Log.i(TAG, "Gc! " + now + " > " + (mFreezeGcPending+1000)); + //Slog.i(TAG, "Gc! " + now + " > " + (mFreezeGcPending+1000)); mH.removeMessages(H.FORCE_GC); Runtime.getRuntime().gc(); mFreezeGcPending = now; @@ -10681,6 +10997,8 @@ public class WindowManagerService extends IWindowManager.Stub mFreezeGcPending = now; } + if (DEBUG_FREEZE) Slog.v(TAG, "*** FREEZING DISPLAY", new RuntimeException()); + mDisplayFrozen = true; if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET; @@ -10700,6 +11018,12 @@ public class WindowManagerService extends IWindowManager.Stub return; } + if (mWaitingForConfig || mAppsFreezingScreen > 0 || mWindowsFreezingScreen) { + return; + } + + if (DEBUG_FREEZE) Slog.v(TAG, "*** UNFREEZING DISPLAY", new RuntimeException()); + mDisplayFrozen = false; mH.removeMessages(H.APP_FREEZE_TIMEOUT); if (PROFILE_ORIENTATION) { @@ -10714,6 +11038,14 @@ public class WindowManagerService extends IWindowManager.Stub mKeyWaiter.notifyAll(); } + // While the display is frozen we don't re-compute the orientation + // to avoid inconsistent states. However, something interesting + // could have actually changed during that time so re-evaluate it + // now to catch that. + if (updateOrientationFromAppTokensLocked()) { + mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } + // A little kludge: a lot could have happened while the // display was frozen, so now that we are coming back we // do a gc so that any remote references the system @@ -10736,6 +11068,10 @@ public class WindowManagerService extends IWindowManager.Stub return; } + pw.println("Input State:"); + mQueue.dump(pw, " "); + pw.println(" "); + synchronized(mWindowMap) { pw.println("Current Window Manager state:"); for (int i=mWindows.size()-1; i>=0; i--) { @@ -10889,7 +11225,9 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget); pw.print(" mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget); } - pw.print(" mInTouchMode="); pw.println(mInTouchMode); + pw.print(" mCurConfiguration="); pw.println(this.mCurConfiguration); + pw.print(" mInTouchMode="); pw.print(mInTouchMode); + pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); pw.print(" mSystemBooted="); pw.print(mSystemBooted); pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled); pw.print(" mLayoutNeeded="); pw.print(mLayoutNeeded); @@ -10897,7 +11235,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mDimAnimator != null) { mDimAnimator.printTo(pw); } else { - pw.print( " no DimAnimator "); + pw.println( " no DimAnimator "); } pw.print(" mInputMethodAnimLayerAdjustment="); pw.print(mInputMethodAnimLayerAdjustment); @@ -10907,7 +11245,8 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY); pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen); pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen); - pw.print(" mAppsFreezingScreen="); pw.println(mAppsFreezingScreen); + pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen); + pw.print(" mWaitingForConfig="); pw.println(mWaitingForConfig); pw.print(" mRotation="); pw.print(mRotation); pw.print(", mForcedAppOrientation="); pw.print(mForcedAppOrientation); pw.print(", mRequestedRotation="); pw.println(mRequestedRotation); @@ -10966,10 +11305,10 @@ public class WindowManagerService extends IWindowManager.Stub public void virtualKeyFeedback(KeyEvent event) { mPolicy.keyFeedbackFromInput(event); } - + /** * DimAnimator class that controls the dim animation. This holds the surface and - * all state used for dim animation. + * all state used for dim animation. */ private static class DimAnimator { Surface mDimSurface; @@ -10978,16 +11317,20 @@ public class WindowManagerService extends IWindowManager.Stub float mDimTargetAlpha; float mDimDeltaPerMs; long mLastDimAnimTime; + + int mLastDimWidth, mLastDimHeight; DimAnimator (SurfaceSession session) { if (mDimSurface == null) { - if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface + ": CREATE"); try { - mDimSurface = new Surface(session, 0, -1, 16, 16, PixelFormat.OPAQUE, + mDimSurface = new Surface(session, 0, + "DimSurface", + -1, 16, 16, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); } catch (Exception e) { - Log.e(TAG, "Exception creating Dim surface", e); + Slog.e(TAG, "Exception creating Dim surface", e); } } } @@ -10996,15 +11339,23 @@ public class WindowManagerService extends IWindowManager.Stub * Show the dim surface. */ void show(int dw, int dh) { - if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" + - dw + "x" + dh + ")"); - mDimShown = true; - try { - mDimSurface.setPosition(0, 0); + if (!mDimShown) { + if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" + + dw + "x" + dh + ")"); + mDimShown = true; + try { + mLastDimWidth = dw; + mLastDimHeight = dh; + mDimSurface.setPosition(0, 0); + mDimSurface.setSize(dw, dh); + mDimSurface.show(); + } catch (RuntimeException e) { + Slog.w(TAG, "Failure showing dim surface", e); + } + } else if (mLastDimWidth != dw || mLastDimHeight != dh) { + mLastDimWidth = dw; + mLastDimHeight = dh; mDimSurface.setSize(dw, dh); - mDimSurface.show(); - } catch (RuntimeException e) { - Log.w(TAG, "Failure showing dim surface", e); } } @@ -11016,7 +11367,7 @@ public class WindowManagerService extends IWindowManager.Stub mDimSurface.setLayer(w.mAnimLayer-1); final float target = w.mExiting ? 0 : w.mAttrs.dimAmount; - if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface + if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface + ": layer=" + (w.mAnimLayer-1) + " target=" + target); if (mDimTargetAlpha != target) { // If the desired dim level has changed, then @@ -11040,7 +11391,7 @@ public class WindowManagerService extends IWindowManager.Stub mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) / duration; } } - + /** * Updating the surface's alpha. Returns true if the animation continues, or returns * false when the animation is finished and the dim surface is hidden. @@ -11053,7 +11404,7 @@ public class WindowManagerService extends IWindowManager.Stub mDimDeltaPerMs = (-mDimCurrentAlpha) / DEFAULT_DIM_DURATION; } } - + boolean animating = false; if (mLastDimAnimTime != 0) { mDimCurrentAlpha += mDimDeltaPerMs @@ -11076,7 +11427,7 @@ public class WindowManagerService extends IWindowManager.Stub // Do we need to continue animating? if (more) { - if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface + ": alpha=" + mDimCurrentAlpha); mLastDimAnimTime = currentTime; mDimSurface.setAlpha(mDimCurrentAlpha); @@ -11084,16 +11435,16 @@ public class WindowManagerService extends IWindowManager.Stub } else { mDimCurrentAlpha = mDimTargetAlpha; mLastDimAnimTime = 0; - if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface + ": final alpha=" + mDimCurrentAlpha); mDimSurface.setAlpha(mDimCurrentAlpha); if (!dimming) { - if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface + if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface + ": HIDE"); try { mDimSurface.hide(); } catch (RuntimeException e) { - Log.w(TAG, "Illegal argument exception hiding dim surface"); + Slog.w(TAG, "Illegal argument exception hiding dim surface"); } mDimShown = false; } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 9e9552a..8383ca3 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -25,6 +25,8 @@ import com.android.server.SystemServer; import com.android.server.Watchdog; import com.android.server.WindowManagerService; +import dalvik.system.Zygote; + import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerNative; @@ -33,6 +35,7 @@ import android.app.AlertDialog; import android.app.ApplicationErrorReport; import android.app.Dialog; import android.app.IActivityController; +import android.app.IActivityManager; import android.app.IActivityWatcher; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; @@ -43,8 +46,9 @@ import android.app.Notification; import android.app.PendingIntent; import android.app.ResultInfo; import android.app.Service; -import android.backup.IBackupManager; +import android.app.backup.IBackupManager; import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -59,6 +63,7 @@ import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PathPermission; import android.content.pm.ProviderInfo; @@ -68,9 +73,12 @@ import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Debug; +import android.os.DropBoxManager; import android.os.Environment; +import android.os.FileObserver; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; @@ -86,14 +94,10 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.provider.Checkin; import android.provider.Settings; -import android.server.data.CrashData; -import android.server.data.StackTraceElementData; -import android.server.data.ThrowableData; -import android.text.TextUtils; import android.util.Config; import android.util.EventLog; +import android.util.Slog; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.SparseArray; @@ -103,15 +107,11 @@ import android.view.View; import android.view.WindowManager; import android.view.WindowManagerPolicy; -import dalvik.system.Zygote; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; import java.io.File; import java.io.FileDescriptor; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStreamReader; import java.io.PrintWriter; import java.lang.IllegalStateException; import java.lang.ref.WeakReference; @@ -122,6 +122,9 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor { static final String TAG = "ActivityManager"; @@ -138,6 +141,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final boolean DEBUG_VISBILITY = localLOGV || false; static final boolean DEBUG_PROCESSES = localLOGV || false; static final boolean DEBUG_PROVIDER = localLOGV || false; + static final boolean DEBUG_URI_PERMISSION = localLOGV || false; static final boolean DEBUG_USER_LEAVING = localLOGV || false; static final boolean DEBUG_RESULTS = localLOGV || false; static final boolean DEBUG_BACKUP = localLOGV || false; @@ -152,44 +156,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final long MONITOR_CPU_MAX_TIME = 0x0fffffff; // wait possibly forever for next cpu sample. static final boolean MONITOR_THREAD_CPU_USAGE = false; - // Event log tags - static final int LOG_CONFIGURATION_CHANGED = 2719; - static final int LOG_CPU = 2721; - static final int LOG_AM_FINISH_ACTIVITY = 30001; - static final int LOG_TASK_TO_FRONT = 30002; - static final int LOG_AM_NEW_INTENT = 30003; - static final int LOG_AM_CREATE_TASK = 30004; - static final int LOG_AM_CREATE_ACTIVITY = 30005; - static final int LOG_AM_RESTART_ACTIVITY = 30006; - static final int LOG_AM_RESUME_ACTIVITY = 30007; - static final int LOG_ANR = 30008; - static final int LOG_ACTIVITY_LAUNCH_TIME = 30009; - static final int LOG_AM_PROCESS_BOUND = 30010; - static final int LOG_AM_PROCESS_DIED = 30011; - static final int LOG_AM_FAILED_TO_PAUSE_ACTIVITY = 30012; - static final int LOG_AM_PAUSE_ACTIVITY = 30013; - static final int LOG_AM_PROCESS_START = 30014; - static final int LOG_AM_PROCESS_BAD = 30015; - static final int LOG_AM_PROCESS_GOOD = 30016; - static final int LOG_AM_LOW_MEMORY = 30017; - static final int LOG_AM_DESTROY_ACTIVITY = 30018; - static final int LOG_AM_RELAUNCH_RESUME_ACTIVITY = 30019; - static final int LOG_AM_RELAUNCH_ACTIVITY = 30020; - static final int LOG_AM_KILL_FOR_MEMORY = 30023; - static final int LOG_AM_BROADCAST_DISCARD_FILTER = 30024; - 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_AM_PROCESS_START_TIMEOUT = 30037; - - static final int LOG_BOOT_PROGRESS_AMS_READY = 3040; - static final int LOG_BOOT_PROGRESS_ENABLE_SCREEN = 3050; - // The flags that are set for all calls we make to the package manager. static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES; @@ -295,41 +261,36 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // the first to go! Value set in system/rootdir/init.rc on startup. // This value is initalized in the constructor, careful when refering to // this static variable externally. - static int EMPTY_APP_ADJ; - - // This is a process with a content provider that does not have any clients - // attached to it. If it did have any clients, its adjustment would be the - // one for the highest-priority of those processes. - static int CONTENT_PROVIDER_ADJ; + static final int EMPTY_APP_ADJ; // This is a process only hosting activities that are not visible, // so it can be killed without any disruption. Value set in // system/rootdir/init.rc on startup. - final int HIDDEN_APP_MAX_ADJ; + static 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; + static final int HOME_APP_ADJ; // This is a process currently hosting a backup operation. Killing it // is not entirely fatal but is generally a bad idea. - final int BACKUP_APP_ADJ; + static final int BACKUP_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. - final int SECONDARY_SERVER_ADJ; + static final int SECONDARY_SERVER_ADJ; // This is a process only hosting activities that are visible to the // user, so we'd prefer they don't disappear. Value set in // system/rootdir/init.rc on startup. - final int VISIBLE_APP_ADJ; + static final int VISIBLE_APP_ADJ; // This is the process running the current foreground app. We'd really // rather not kill it! Value set in system/rootdir/init.rc on startup. - final int FOREGROUND_APP_ADJ; + static final int FOREGROUND_APP_ADJ; // This is a process running a core server, such as telephony. Definitely // don't want to kill it, but doing so is not completely fatal. @@ -341,22 +302,66 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Memory pages are 4K. static final int PAGE_SIZE = 4*1024; - // System property defining error report receiver for system apps - static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps"; - - // System property defining default error report receiver - static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default"; - // Corresponding memory levels for above adjustments. - final int EMPTY_APP_MEM; - final int HIDDEN_APP_MEM; - final int HOME_APP_MEM; - final int BACKUP_APP_MEM; - final int SECONDARY_SERVER_MEM; - final int VISIBLE_APP_MEM; - final int FOREGROUND_APP_MEM; + static final int EMPTY_APP_MEM; + static final int HIDDEN_APP_MEM; + static final int HOME_APP_MEM; + static final int BACKUP_APP_MEM; + static final int SECONDARY_SERVER_MEM; + static final int VISIBLE_APP_MEM; + static final int FOREGROUND_APP_MEM; + + // The minimum number of hidden apps we want to be able to keep around, + // without empty apps being able to push them out of memory. + static final int MIN_HIDDEN_APPS = 2; + + // The maximum number of hidden processes we will keep around before + // killing them; this is just a control to not let us go too crazy with + // keeping around processes on devices with large amounts of RAM. + static final int MAX_HIDDEN_APPS = 15; - final int MY_PID; + // We put empty content processes after any hidden processes that have + // been idle for less than 30 seconds. + static final long CONTENT_APP_IDLE_OFFSET = 30*1000; + + // We put empty content processes after any hidden processes that have + // been idle for less than 60 seconds. + static final long EMPTY_APP_IDLE_OFFSET = 60*1000; + + static { + // These values are set in system/rootdir/init.rc on startup. + FOREGROUND_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_ADJ")); + VISIBLE_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ")); + SECONDARY_SERVER_ADJ = + Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ")); + BACKUP_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_ADJ")); + HOME_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.HOME_APP_ADJ")); + HIDDEN_APP_MIN_ADJ = + Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MIN_ADJ")); + EMPTY_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_ADJ")); + HIDDEN_APP_MAX_ADJ = EMPTY_APP_ADJ-1; + FOREGROUND_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_MEM"))*PAGE_SIZE; + VISIBLE_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE; + SECONDARY_SERVER_MEM = + Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; + BACKUP_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_MEM"))*PAGE_SIZE; + HOME_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.HOME_APP_MEM"))*PAGE_SIZE; + HIDDEN_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MEM"))*PAGE_SIZE; + EMPTY_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_MEM"))*PAGE_SIZE; + } + + static final int MY_PID = Process.myPid(); static final String[] EMPTY_STRING_ARRAY = new String[0]; @@ -394,6 +399,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = new ArrayList<PendingActivityLaunch>(); /** + * List of people waiting to find out about the next launched activity. + */ + final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched + = new ArrayList<IActivityManager.WaitResult>(); + + /** + * List of people waiting to find out about the next visible activity. + */ + final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible + = new ArrayList<IActivityManager.WaitResult>(); + + /** * List of all active broadcasts that are to be executed immediately * (without waiting for another broadcast to finish). Currently this only * contains broadcasts to registered receivers, to avoid spinning up @@ -568,7 +585,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * It contains ApplicationRecord objects. This list does NOT include * any persistent application records (since we never want to exit them). */ - final ArrayList<ProcessRecord> mLRUProcesses + final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>(); /** @@ -766,6 +783,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Configuration mConfiguration = new Configuration(); /** + * Current sequencing integer of the configuration, for skipping old + * configurations. + */ + int mConfigurationSeq = 0; + + /** + * Set when we know we are going to be calling updateConfiguration() + * soon, so want to skip intermediate config checks. + */ + boolean mConfigWillChange; + + /** * Hardware-reported OpenGLES version. */ final int GL_ES_VERSION; @@ -905,7 +934,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ final ProcessStats mProcessStats = new ProcessStats( MONITOR_THREAD_CPU_USAGE); - long mLastCpuTime = 0; + final AtomicLong mLastCpuTime = new AtomicLong(0); + final AtomicBoolean mProcessStatsMutexFree = new AtomicBoolean(true); + long mLastWriteTime = 0; long mInitialStartTime = 0; @@ -929,7 +960,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen AppDeathRecipient(ProcessRecord app, int pid, IApplicationThread thread) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "New death recipient " + this + " for thread " + thread.asBinder()); mApp = app; @@ -938,7 +969,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } public void binderDied() { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Death received in " + this + " for thread " + mAppThread.asBinder()); removeRequestedPss(mApp); @@ -965,42 +996,32 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final int IM_FEELING_LUCKY_MSG = 15; static final int LAUNCH_TIMEOUT_MSG = 16; static final int DESTROY_TIMEOUT_MSG = 17; - static final int SERVICE_ERROR_MSG = 18; static final int RESUME_TOP_ACTIVITY_MSG = 19; static final int PROC_START_TIMEOUT_MSG = 20; static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21; static final int KILL_APPLICATION_MSG = 22; + static final int FINALIZE_PENDING_INTENT_MSG = 23; AlertDialog mUidAlert; final Handler mHandler = new Handler() { //public Handler() { - // if (localLOGV) Log.v(TAG, "Handler started!"); + // if (localLOGV) Slog.v(TAG, "Handler started!"); //} public void handleMessage(Message msg) { switch (msg.what) { case SHOW_ERROR_MSG: { HashMap data = (HashMap) msg.obj; - byte[] crashData = (byte[])data.get("crashData"); - if (crashData != null) { - // This needs to be *un*synchronized to avoid deadlock. - ContentResolver resolver = mContext.getContentResolver(); - Checkin.reportCrash(resolver, crashData); - } synchronized (ActivityManagerService.this) { ProcessRecord proc = (ProcessRecord)data.get("app"); if (proc != null && proc.crashDialog != null) { - Log.e(TAG, "App already has crash dialog: " + proc); + Slog.e(TAG, "App already has crash dialog: " + proc); return; } AppErrorResult res = (AppErrorResult) data.get("result"); if (!mSleeping && !mShuttingDown) { - Dialog d = new AppErrorDialog( - mContext, res, proc, - (Integer)data.get("flags"), - (String)data.get("shortMsg"), - (String)data.get("longMsg")); + Dialog d = new AppErrorDialog(mContext, res, proc); d.show(); proc.crashDialog = d; } else { @@ -1017,7 +1038,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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); + Slog.e(TAG, "App already has anr dialog: " + proc); return; } @@ -1069,7 +1090,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } break; case BROADCAST_INTENT_MSG: { - if (DEBUG_BROADCAST) Log.v( + if (DEBUG_BROADCAST) Slog.v( TAG, "Received BROADCAST_INTENT_MSG"); processNextBroadcast(true); } break; @@ -1086,7 +1107,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen IBinder token = (IBinder)msg.obj; // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. - Log.w(TAG, "Activity pause timeout for " + token); + Slog.w(TAG, "Activity pause timeout for " + token); activityPaused(token, null, true); } break; case IDLE_TIMEOUT_MSG: { @@ -1100,14 +1121,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. IBinder token = (IBinder)msg.obj; - Log.w(TAG, "Activity idle timeout for " + token); + Slog.w(TAG, "Activity idle timeout for " + token); activityIdleInternal(token, true, null); } break; case DESTROY_TIMEOUT_MSG: { IBinder token = (IBinder)msg.obj; // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. - Log.w(TAG, "Activity destroy timeout for " + token); + Slog.w(TAG, "Activity destroy timeout for " + token); activityDestroyed(token); } break; case IDLE_NOW_MSG: { @@ -1126,13 +1147,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } break; case UPDATE_TIME_ZONE: { synchronized (ActivityManagerService.this) { - for (int i = mLRUProcesses.size() - 1 ; i >= 0 ; i--) { - ProcessRecord r = mLRUProcesses.get(i); + for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { + ProcessRecord r = mLruProcesses.get(i); if (r.thread != null) { try { r.thread.updateTimeZone(); } catch (RemoteException ex) { - Log.w(TAG, "Failed to update time zone for: " + r.info.processName); + Slog.w(TAG, "Failed to update time zone for: " + r.info.processName); } } } @@ -1165,18 +1186,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized (ActivityManagerService.this) { if (mLaunchingActivity.isHeld()) { - Log.w(TAG, "Launch timeout has expired, giving up wake lock!"); + Slog.w(TAG, "Launch timeout has expired, giving up wake lock!"); mLaunchingActivity.release(); } } } break; - case SERVICE_ERROR_MSG: { - ServiceRecord srv = (ServiceRecord)msg.obj; - // This needs to be *un*synchronized to avoid deadlock. - Checkin.logEvent(mContext.getContentResolver(), - Checkin.Events.Tag.SYSTEM_SERVICE_LOOPING, - srv.name.toShortString()); - } break; case RESUME_TOP_ACTIVITY_MSG: { synchronized (ActivityManagerService.this) { resumeTopActivityLocked(null); @@ -1205,9 +1219,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int uid = msg.arg1; boolean restart = (msg.arg2 == 1); String pkg = (String) msg.obj; - uninstallPackageLocked(pkg, uid, restart); + forceStopPackageLocked(pkg, uid, restart, false, true); } } break; + case FINALIZE_PENDING_INTENT_MSG: { + ((PendingIntentRecord)msg.obj).completeFinalize(); + } break; } } }; @@ -1221,10 +1238,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (MONITOR_CPU_USAGE) { ServiceManager.addService("cpuinfo", new CpuBinder(m)); } - ServiceManager.addService("activity.broadcasts", new BroadcastsBinder(m)); - ServiceManager.addService("activity.services", new ServicesBinder(m)); - ServiceManager.addService("activity.senders", new SendersBinder(m)); - ServiceManager.addService("activity.providers", new ProvidersBinder(m)); ServiceManager.addService("permission", new PermissionController(m)); ApplicationInfo info = @@ -1237,13 +1250,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mSystemThread.getApplicationThread(), info, info.processName); app.persistent = true; - app.pid = Process.myPid(); + app.pid = MY_PID; app.maxAdj = SYSTEM_ADJ; mSelf.mProcessNames.put(app.processName, app.info.uid, app); synchronized (mSelf.mPidsSelfLocked) { mSelf.mPidsSelfLocked.put(app.pid, app); } - mSelf.updateLRUListLocked(app, true); + mSelf.updateLruProcessLocked(app, true, true); } } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException( @@ -1332,54 +1345,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - static class BroadcastsBinder extends Binder { - ActivityManagerService mActivityManagerService; - BroadcastsBinder(ActivityManagerService activityManagerService) { - mActivityManagerService = activityManagerService; - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mActivityManagerService.dumpBroadcasts(pw); - } - } - - static class ServicesBinder extends Binder { - ActivityManagerService mActivityManagerService; - ServicesBinder(ActivityManagerService activityManagerService) { - mActivityManagerService = activityManagerService; - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mActivityManagerService.dumpServices(pw); - } - } - - static class SendersBinder extends Binder { - ActivityManagerService mActivityManagerService; - SendersBinder(ActivityManagerService activityManagerService) { - mActivityManagerService = activityManagerService; - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mActivityManagerService.dumpSenders(pw); - } - } - - static class ProvidersBinder extends Binder { - ActivityManagerService mActivityManagerService; - ProvidersBinder(ActivityManagerService activityManagerService) { - mActivityManagerService = activityManagerService; - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mActivityManagerService.dumpProviders(pw); - } - } - static class MemBinder extends Binder { ActivityManagerService mActivityManagerService; MemBinder(ActivityManagerService activityManagerService) { @@ -1400,8 +1365,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (NumberFormatException e) { } - for (int i=0; i<service.mLRUProcesses.size(); i++) { - ProcessRecord proc = service.mLRUProcesses.get(i); + for (int i=service.mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord proc = service.mLruProcesses.get(i); if (proc.pid == pid) { procs.add(proc); } else if (proc.processName.equals(args[0])) { @@ -1413,7 +1378,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } } else { - procs = service.mLRUProcesses; + procs = new ArrayList<ProcessRecord>(service.mLruProcesses); } } dumpApplicationMemoryUsage(fd, pw, procs, " ", args); @@ -1444,9 +1409,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mSimpleProcessManagement = true; } - Log.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass()); - - MY_PID = Process.myPid(); + Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass()); File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); @@ -1462,45 +1425,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", ConfigurationInfo.GL_ES_VERSION_UNDEFINED); - mConfiguration.makeDefault(); + mConfiguration.setToDefaults(); + mConfiguration.locale = Locale.getDefault(); mProcessStats.init(); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); - // These values are set in system/rootdir/init.rc on startup. - FOREGROUND_APP_ADJ = - Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_ADJ")); - VISIBLE_APP_ADJ = - Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ")); - SECONDARY_SERVER_ADJ = - Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ")); - BACKUP_APP_ADJ = - Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_ADJ")); - HOME_APP_ADJ = - Integer.valueOf(SystemProperties.get("ro.HOME_APP_ADJ")); - HIDDEN_APP_MIN_ADJ = - Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MIN_ADJ")); - CONTENT_PROVIDER_ADJ = - Integer.valueOf(SystemProperties.get("ro.CONTENT_PROVIDER_ADJ")); - HIDDEN_APP_MAX_ADJ = CONTENT_PROVIDER_ADJ-1; - EMPTY_APP_ADJ = - Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_ADJ")); - FOREGROUND_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_MEM"))*PAGE_SIZE; - VISIBLE_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE; - SECONDARY_SERVER_MEM = - Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; - BACKUP_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_MEM"))*PAGE_SIZE; - HOME_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.HOME_APP_MEM"))*PAGE_SIZE; - HIDDEN_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MEM"))*PAGE_SIZE; - EMPTY_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_MEM"))*PAGE_SIZE; - mProcessStatsThread = new Thread("ProcessStats") { public void run() { while (true) { @@ -1508,23 +1439,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { synchronized(this) { final long now = SystemClock.uptimeMillis(); - long nextCpuDelay = (mLastCpuTime+MONITOR_CPU_MAX_TIME)-now; + long nextCpuDelay = (mLastCpuTime.get()+MONITOR_CPU_MAX_TIME)-now; long nextWriteDelay = (mLastWriteTime+BATTERY_STATS_TIME)-now; - //Log.i(TAG, "Cpu delay=" + nextCpuDelay + //Slog.i(TAG, "Cpu delay=" + nextCpuDelay // + ", write delay=" + nextWriteDelay); if (nextWriteDelay < nextCpuDelay) { nextCpuDelay = nextWriteDelay; } if (nextCpuDelay > 0) { + mProcessStatsMutexFree.set(true); this.wait(nextCpuDelay); } } } catch (InterruptedException e) { } - updateCpuStatsNow(); } catch (Exception e) { - Log.e(TAG, "Unexpected exception collecting process stats", e); + Slog.e(TAG, "Unexpected exception collecting process stats", e); } } } @@ -1541,36 +1472,40 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // The activity manager only throws security exceptions, so let's // log all others. if (!(e instanceof SecurityException)) { - Log.e(TAG, "Activity Manager Crash", e); + Slog.e(TAG, "Activity Manager Crash", e); } throw e; } } void updateCpuStats() { - synchronized (mProcessStatsThread) { - final long now = SystemClock.uptimeMillis(); - if (mLastCpuTime < (now-MONITOR_CPU_MIN_TIME)) { + final long now = SystemClock.uptimeMillis(); + if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) { + return; + } + if (mProcessStatsMutexFree.compareAndSet(true, false)) { + synchronized (mProcessStatsThread) { mProcessStatsThread.notify(); } } } - + void updateCpuStatsNow() { synchronized (mProcessStatsThread) { + mProcessStatsMutexFree.set(false); final long now = SystemClock.uptimeMillis(); boolean haveNewCpuStats = false; if (MONITOR_CPU_USAGE && - mLastCpuTime < (now-MONITOR_CPU_MIN_TIME)) { - mLastCpuTime = now; + mLastCpuTime.get() < (now-MONITOR_CPU_MIN_TIME)) { + mLastCpuTime.set(now); haveNewCpuStats = true; mProcessStats.update(); - //Log.i(TAG, mProcessStats.printCurrentState()); - //Log.i(TAG, "Total CPU usage: " + //Slog.i(TAG, mProcessStats.printCurrentState()); + //Slog.i(TAG, "Total CPU usage: " // + mProcessStats.getTotalCpuPercent() + "%"); - // Log the cpu usage if the property is set. + // Slog the cpu usage if the property is set. if ("true".equals(SystemProperties.get("events.cpu"))) { int user = mProcessStats.getLastUserTime(); int system = mProcessStats.getLastSystemTime(); @@ -1582,7 +1517,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int total = user + system + iowait + irq + softIrq + idle; if (total == 0) total = 1; - EventLog.writeEvent(LOG_CPU, + EventLog.writeEvent(EventLogTags.CPU, ((user+system+iowait+irq+softIrq) * 100) / total, (user * 100) / total, (system * 100) / total, @@ -1653,13 +1588,55 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void updateLRUListLocked(ProcessRecord app, - boolean oomAdj) { + private final void updateLruProcessLocked(ProcessRecord app, + boolean oomAdj, boolean updateActivityTime) { // put it on the LRU to keep track of when it should be exited. - int lrui = mLRUProcesses.indexOf(app); - if (lrui >= 0) mLRUProcesses.remove(lrui); - mLRUProcesses.add(app); - //Log.i(TAG, "Putting proc to front: " + app.processName); + int lrui = mLruProcesses.indexOf(app); + if (lrui >= 0) mLruProcesses.remove(lrui); + + int i = mLruProcesses.size()-1; + int skipTop = 0; + + // compute the new weight for this process. + if (updateActivityTime) { + app.lastActivityTime = SystemClock.uptimeMillis(); + } + if (app.activities.size() > 0) { + // If this process has activities, we more strongly want to keep + // it around. + app.lruWeight = app.lastActivityTime; + } else if (app.pubProviders.size() > 0) { + // If this process contains content providers, we want to keep + // it a little more strongly. + app.lruWeight = app.lastActivityTime - CONTENT_APP_IDLE_OFFSET; + // Also don't let it kick out the first few "real" hidden processes. + skipTop = MIN_HIDDEN_APPS; + } else { + // If this process doesn't have activities, we less strongly + // want to keep it around, and generally want to avoid getting + // in front of any very recently used activities. + app.lruWeight = app.lastActivityTime - EMPTY_APP_IDLE_OFFSET; + // Also don't let it kick out the first few "real" hidden processes. + skipTop = MIN_HIDDEN_APPS; + } + while (i >= 0) { + ProcessRecord p = mLruProcesses.get(i); + // If this app shouldn't be in front of the first N background + // apps, then skip over that many that are currently hidden. + if (skipTop > 0 && p.setAdj >= HIDDEN_APP_MIN_ADJ) { + skipTop--; + } + if (p.lruWeight <= app.lruWeight){ + mLruProcesses.add(i+1, app); + break; + } + i--; + } + if (i < 0) { + mLruProcesses.add(0, app); + } + + //Slog.i(TAG, "Putting proc to front: " + app.processName); if (oomAdj) { updateOomAdjLocked(); } @@ -1770,13 +1747,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app = app; - if (localLOGV) Log.v(TAG, "Launching: " + r); + if (localLOGV) Slog.v(TAG, "Launching: " + r); int idx = app.activities.indexOf(r); if (idx < 0) { app.activities.add(r); } - updateLRUListLocked(app, true); + updateLruProcessLocked(app, true, true); try { if (app.thread == null) { @@ -1788,12 +1765,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen results = r.results; newIntents = r.newIntents; } - if (DEBUG_SWITCH) Log.v(TAG, "Launching: " + r + if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r + " icicle=" + r.icicle + " with results=" + results + " newIntents=" + newIntents + " andResume=" + andResume); if (andResume) { - EventLog.writeEvent(LOG_AM_RESTART_ACTIVITY, + EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, System.identityHashCode(r), r.task.taskId, r.shortComponentName); } @@ -1809,7 +1786,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.launchFailed) { // This is the second time we failed -- finish activity // and give up. - Log.e(TAG, "Second failure launching " + Slog.e(TAG, "Second failure launching " + r.intent.getComponent().flattenToShortString() + ", giving up", e); appDiedLocked(app, app.pid, app.thread); @@ -1826,7 +1803,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.launchFailed = false; if (updateLRUListLocked(r)) { - Log.w(TAG, "Activity " + r + Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list"); } @@ -1879,7 +1856,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { - Log.w(TAG, "Exception when starting activity " + Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e); } @@ -1901,12 +1878,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // object attached to it so we know it couldn't have crashed; and // (3) There is a pid assigned to it, so it is either starting or // already running. - if (DEBUG_PROCESSES) Log.v(TAG, "startProcess: name=" + processName + if (DEBUG_PROCESSES) Slog.v(TAG, "startProcess: name=" + processName + " app=" + app + " knownToBeDead=" + knownToBeDead + " thread=" + (app != null ? app.thread : null) + " pid=" + (app != null ? app.pid : -1)); if (app != null && app.pid > 0) { if (!knownToBeDead || app.thread == null) { + // We already have the app running, or are waiting for it to + // come up (we have a pid but not yet its thread), so keep it. return app; } else { // An application record is attached to a previous process, @@ -1931,7 +1910,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // if it had been bad. mProcessCrashTimes.remove(info.processName, info.uid); if (mBadProcesses.get(info.processName, info.uid) != null) { - EventLog.writeEvent(LOG_AM_PROCESS_GOOD, info.uid, + EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid, info.processName); mBadProcesses.remove(info.processName, info.uid); if (app != null) { @@ -1991,7 +1970,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen gids = mContext.getPackageManager().getPackageGids( app.info.packageName); } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Unable to retrieve gids", e); + Slog.w(TAG, "Unable to retrieve gids", e); } if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) { if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL @@ -2008,6 +1987,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; } + // Run the app in safe mode if its manifest requests so or the + // system is booted in safe mode. + if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 || + Zygote.systemInSafeMode == true) { + debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE; + } if ("1".equals(SystemProperties.get("debug.checkjni"))) { debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; } @@ -2024,7 +2009,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - EventLog.writeEvent(LOG_AM_PROCESS_START, pid, uid, + EventLog.writeEvent(EventLogTags.AM_PROC_START, pid, uid, app.processName, hostingType, hostingNameStr != null ? hostingNameStr : ""); @@ -2055,7 +2040,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } buf.append("}"); - Log.i(TAG, buf.toString()); + Slog.i(TAG, buf.toString()); if (pid == 0 || pid == MY_PID) { // Processes are being emulated with threads. app.pid = MY_PID; @@ -2075,29 +2060,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen RuntimeException e = new RuntimeException( "Failure starting process " + app.processName + ": returned pid=" + pid); - Log.e(TAG, e.getMessage(), e); + Slog.e(TAG, e.getMessage(), e); } } catch (RuntimeException e) { // XXX do better error recovery. app.pid = 0; - Log.e(TAG, "Failure starting process " + app.processName, e); + Slog.e(TAG, "Failure starting process " + app.processName, e); } } 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 " + Slog.e(TAG, "Trying to pause when pause is already pending for " + mPausingActivity, e); } HistoryRecord prev = mResumedActivity; if (prev == null) { RuntimeException e = new RuntimeException(); - Log.e(TAG, "Trying to pause when nothing is resumed", e); + Slog.e(TAG, "Trying to pause when nothing is resumed", e); resumeTopActivityLocked(null); return; } - if (DEBUG_PAUSE) Log.v(TAG, "Start pausing: " + prev); + if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev); mResumedActivity = null; mPausingActivity = prev; mLastPausedActivity = prev; @@ -2107,9 +2092,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen updateCpuStats(); if (prev.app != null && prev.app.thread != null) { - if (DEBUG_PAUSE) Log.v(TAG, "Enqueueing pending pause: " + prev); + if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev); try { - EventLog.writeEvent(LOG_AM_PAUSE_ACTIVITY, + EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY, System.identityHashCode(prev), prev.shortComponentName); prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving, @@ -2117,7 +2102,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen updateUsageStats(prev, false); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. - Log.w(TAG, "Exception thrown during pause", e); + Slog.w(TAG, "Exception thrown during pause", e); mPausingActivity = null; mLastPausedActivity = null; } @@ -2146,7 +2131,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (!uiSleeping) { prev.pauseKeyDispatchingLocked(); } else { - if (DEBUG_PAUSE) Log.v(TAG, "Key dispatch not paused for screen off"); + if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off"); } // Schedule a pause timeout in case the app doesn't respond. @@ -2155,29 +2140,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG); msg.obj = prev; mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT); - if (DEBUG_PAUSE) Log.v(TAG, "Waiting for pause to complete..."); + if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete..."); } else { // This activity failed to schedule the // pause, so just treat it as being paused now. - if (DEBUG_PAUSE) Log.v(TAG, "Activity not running, resuming next."); + if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next."); resumeTopActivityLocked(null); } } private final void completePauseLocked() { HistoryRecord prev = mPausingActivity; - if (DEBUG_PAUSE) Log.v(TAG, "Complete pause: " + prev); + if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev); if (prev != null) { if (prev.finishing) { - if (DEBUG_PAUSE) Log.v(TAG, "Executing finish of activity: " + prev); + if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev); prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE); } else if (prev.app != null) { - if (DEBUG_PAUSE) Log.v(TAG, "Enqueueing pending stop: " + prev); + if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev); if (prev.waitingVisible) { prev.waitingVisible = false; mWaitingVisibleActivities.remove(prev); - if (DEBUG_SWITCH || DEBUG_PAUSE) Log.v( + if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v( TAG, "Complete pause, no longer waiting: " + prev); } if (prev.configDestroy) { @@ -2186,7 +2171,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // To juggle the fact that we are also starting a new // instance right now, we need to first completely stop // the current instance before starting the new one. - if (DEBUG_PAUSE) Log.v(TAG, "Destroying after pause: " + prev); + if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev); destroyActivityLocked(prev, true); } else { mStoppingActivities.add(prev); @@ -2194,14 +2179,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If we already have a few activities waiting to stop, // then give up on things going idle and start clearing // them out. - if (DEBUG_PAUSE) Log.v(TAG, "To many pending stops, forcing idle"); + if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle"); Message msg = Message.obtain(); msg.what = ActivityManagerService.IDLE_NOW_MSG; mHandler.sendMessage(msg); } } } else { - if (DEBUG_PAUSE) Log.v(TAG, "App died during pause, not stopping: " + prev); + if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev); prev = null; } mPausingActivity = null; @@ -2294,7 +2279,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ private final void ensureActivitiesVisibleLocked(HistoryRecord top, HistoryRecord starting, String onlyThisProcess, int configChanges) { - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "ensureActivitiesVisible behind " + top + " configChanges=0x" + Integer.toHexString(configChanges)); @@ -2309,7 +2294,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean behindFullscreen = false; for (; i>=0; i--) { r = (HistoryRecord)mHistory.get(i); - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Make visible? " + r + " finishing=" + r.finishing + " state=" + r.state); if (r.finishing) { @@ -2331,13 +2316,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // This activity needs to be visible, but isn't even // running... get it started, but don't resume it // at this point. - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Start and freeze screen for " + r); if (r != starting) { r.startFreezingScreenLocked(r.app, configChanges); } if (!r.visible) { - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Starting and making visible: " + r); mWindowManager.setAppVisibility(r, true); } @@ -2349,7 +2334,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } else if (r.visible) { // If this activity is already visible, then there is nothing // else to do here. - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Skipping: already visible at " + r); r.stopFreezingScreenLocked(false); @@ -2360,7 +2345,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.state != ActivityState.RESUMED && r != starting) { // If this activity is paused, tell it // to now show its window. - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Making visible and scheduling visibility: " + r); try { mWindowManager.setAppVisibility(r, true); @@ -2369,7 +2354,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (Exception e) { // Just skip on any failure; we'll make it // visible when it next restarts. - Log.w(TAG, "Exception thrown making visibile: " + Slog.w(TAG, "Exception thrown making visibile: " + r.intent.getComponent(), e); } } @@ -2380,7 +2365,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.fullscreen) { // At this point, nothing else needs to be shown - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Stopping: fullscreen at " + r); behindFullscreen = true; i--; @@ -2392,14 +2377,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // sure they no longer are keeping the screen frozen. while (i >= 0) { r = (HistoryRecord)mHistory.get(i); - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Make invisible? " + r + " finishing=" + r.finishing + " state=" + r.state + " behindFullscreen=" + behindFullscreen); if (!r.finishing) { if (behindFullscreen) { if (r.visible) { - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Making invisible: " + r); r.visible = false; try { @@ -2407,22 +2392,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if ((r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) && r.app != null && r.app.thread != null) { - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Scheduling invisibility: " + r); r.app.thread.scheduleWindowVisibility(r, false); } } catch (Exception e) { // Just skip on any failure; we'll make it // visible when it next restarts. - Log.w(TAG, "Exception thrown making hidden: " + Slog.w(TAG, "Exception thrown making hidden: " + r.intent.getComponent(), e); } } else { - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Already invisible: " + r); } } else if (r.fullscreen) { - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Now behindFullscreen: " + r); behindFullscreen = true; } @@ -2541,7 +2526,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private void reportResumedActivityLocked(HistoryRecord r) { - //Log.i(TAG, "**** REPORT RESUME: " + r); + //Slog.i(TAG, "**** REPORT RESUME: " + r); final int identHash = System.identityHashCode(r); updateUsageStats(r, true); @@ -2611,19 +2596,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mStoppingActivities.remove(next); mWaitingVisibleActivities.remove(next); - if (DEBUG_SWITCH) Log.v(TAG, "Resuming " + next); + if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next); // If we are currently pausing an activity, then don't do anything // until that is done. if (mPausingActivity != null) { - if (DEBUG_SWITCH) Log.v(TAG, "Skip resume: pausing=" + mPausingActivity); + if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity); return false; } // We need to start pausing the current activity so the top one // can be resumed... if (mResumedActivity != null) { - if (DEBUG_SWITCH) Log.v(TAG, "Skip resume: need to start pausing"); + if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing"); startPausingLocked(userLeaving, false); return true; } @@ -2632,7 +2617,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (!prev.waitingVisible && next != null && !next.nowVisible) { prev.waitingVisible = true; mWaitingVisibleActivities.add(prev); - if (DEBUG_SWITCH) Log.v( + if (DEBUG_SWITCH) Slog.v( TAG, "Resuming top, waiting visible to hide: " + prev); } else { // The next activity is already visible, so hide the previous @@ -2645,12 +2630,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // new one is found to be full-screen or not. if (prev.finishing) { mWindowManager.setAppVisibility(prev, false); - if (DEBUG_SWITCH) Log.v(TAG, "Not waiting for visible to hide: " + if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: " + prev + ", waitingVisible=" + (prev != null ? prev.waitingVisible : null) + ", nowVisible=" + next.nowVisible); } else { - if (DEBUG_SWITCH) Log.v(TAG, "Previous already visible but still waiting to hide: " + if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: " + prev + ", waitingVisible=" + (prev != null ? prev.waitingVisible : null) + ", nowVisible=" + next.nowVisible); @@ -2663,7 +2648,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // to ignore it when computing the desired screen orientation. if (prev != null) { if (prev.finishing) { - if (DEBUG_TRANSITION) Log.v(TAG, + if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare close transition: prev=" + prev); if (mNoAnimActivities.contains(prev)) { mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); @@ -2675,7 +2660,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWindowManager.setAppWillBeHidden(prev); mWindowManager.setAppVisibility(prev, false); } else { - if (DEBUG_TRANSITION) Log.v(TAG, + if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: prev=" + prev); if (mNoAnimActivities.contains(next)) { mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); @@ -2690,7 +2675,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWindowManager.setAppVisibility(prev, false); } } else if (mHistory.size() > 1) { - if (DEBUG_TRANSITION) Log.v(TAG, + if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: no previous"); if (mNoAnimActivities.contains(next)) { mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); @@ -2700,7 +2685,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (next.app != null && next.app.thread != null) { - if (DEBUG_SWITCH) Log.v(TAG, "Resume running: " + next); + if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next); // This activity is now becoming visible. mWindowManager.setAppVisibility(next, true); @@ -2713,7 +2698,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen next.state = ActivityState.RESUMED; mResumedActivity = next; next.task.touchActiveTime(); - updateLRUListLocked(next.app, true); + updateLruProcessLocked(next.app, true, true); updateLRUListLocked(next); // Have the window manager re-evaluate the orientation of @@ -2724,20 +2709,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mConfiguration, next.mayFreezeScreenLocked(next.app) ? next : null); if (config != null) { - /* - * Explicitly restore the locale to the one from the - * old configuration, since the one that comes back from - * the window manager has the default (boot) locale. - * - * It looks like previously the locale picker only worked - * by coincidence: usually it would do its setting of - * the locale after the activity transition, so it didn't - * matter that this lost it. With the synchronized - * block now keeping them from happening at the same time, - * this one always would happen second and undo what the - * locale picker had just done. - */ - config.locale = mConfiguration.locale; next.frozenBeforeDestroy = true; } updated = updateConfigurationLocked(config, next); @@ -2749,7 +2720,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // is still at the top and schedule another run if something // weird happened. HistoryRecord nextNext = topRunningActivityLocked(null); - if (DEBUG_SWITCH) Log.i(TAG, + if (DEBUG_SWITCH) Slog.i(TAG, "Activity config changed during resume: " + next + ", new next: " + nextNext); if (nextNext != next) { @@ -2769,7 +2740,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (a != null) { final int N = a.size(); if (!next.finishing && N > 0) { - if (DEBUG_RESULTS) Log.v( + if (DEBUG_RESULTS) Slog.v( TAG, "Delivering results to " + next + ": " + a); next.app.thread.scheduleSendResult(next, a); @@ -2780,7 +2751,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen next.app.thread.scheduleNewIntent(next.newIntents, next); } - EventLog.writeEvent(LOG_AM_RESUME_ACTIVITY, + EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, System.identityHashCode(next), next.task.taskId, next.shortComponentName); @@ -2793,8 +2764,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Whoops, need to restart this activity! next.state = lastState; mResumedActivity = lastResumedActivity; - if (Config.LOGD) Log.d(TAG, - "Restarting because process died: " + next); + Slog.i(TAG, "Restarting because process died: " + next); if (!next.hasBeenLaunched) { next.hasBeenLaunched = true; } else { @@ -2817,7 +2787,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (Exception e) { // If any exception gets thrown, toss away this // activity and try the next one. - Log.w(TAG, "Exception thrown during resume of " + next, e); + Slog.w(TAG, "Exception thrown during resume of " + next, e); requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null, "resume-exception"); return true; @@ -2839,7 +2809,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen next.nonLocalizedLabel, next.labelRes, next.icon, null, true); } - if (DEBUG_SWITCH) Log.v(TAG, "Restarting: " + next); + if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next); } startSpecificActivityLocked(next, true, true); } @@ -2898,7 +2868,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // activity if (addPos < NH) { mUserLeaving = false; - if (DEBUG_USER_LEAVING) Log.v(TAG, "startActivity() behind front, mUserLeaving=false"); + if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false"); } // Slot the activity into the history stack and proceed @@ -2918,7 +2888,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (proc == null || proc.thread == null) { showStartingIcon = true; } - if (DEBUG_TRANSITION) Log.v(TAG, + if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: starting " + r); if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); @@ -3039,7 +3009,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (!ret.finishing) { int index = indexOfTokenLocked(ret); if (index >= 0) { - finishActivityLocked(ret, 0, Activity.RESULT_CANCELED, + finishActivityLocked(ret, index, Activity.RESULT_CANCELED, null, "clear"); } return null; @@ -3101,7 +3071,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); + Slog.w(TAG, "Exception thrown sending new intent to " + r, e); } } if (!sent) { @@ -3125,13 +3095,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen String resultWho, int requestCode, int callingPid, int callingUid, boolean onlyIfNeeded, boolean componentSpecified) { - Log.i(TAG, "Starting activity: " + intent); + Slog.i(TAG, "Starting activity: " + intent); HistoryRecord sourceRecord = null; HistoryRecord resultRecord = null; if (resultTo != null) { int index = indexOfTokenLocked(resultTo); - if (DEBUG_RESULTS) Log.v( + if (DEBUG_RESULTS) Slog.v( TAG, "Sending result to " + resultTo + " (index " + index + ")"); if (index >= 0) { sourceRecord = (HistoryRecord)mHistory.get(index); @@ -3181,7 +3151,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen callingPid = callerApp.pid; callingUid = callerApp.info.uid; } else { - Log.w(TAG, "Unable to find app for caller " + caller + Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + callingPid + ") when starting: " + intent.toString()); err = START_PERMISSION_DENIED; @@ -3209,7 +3179,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " from " + callerApp + " (pid=" + callingPid + ", uid=" + callingUid + ")" + " requires " + aInfo.permission; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } @@ -3297,7 +3267,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // We'll invoke onUserLeaving before onPause only if the launching // activity did not explicitly state that this is an automated launch. mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; - if (DEBUG_USER_LEAVING) Log.v(TAG, + if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving); // If the caller has asked not to resume at this point, we make note @@ -3339,7 +3309,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // This activity is not being started from another... in this // case we -always- start a new task. if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { - Log.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: " + Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent); launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; } @@ -3361,7 +3331,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // is pretty messed up, so instead immediately send back a cancel // and let the new task continue launched as normal without a // dependency on its originator. - Log.w(TAG, "Activity is launching as a new task, so cancelling activity result."); + Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result."); sendActivityResultLocked(-1, r.resultTo, r.resultWho, r.requestCode, Activity.RESULT_CANCELED, null); @@ -3441,7 +3411,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // intent. top.task.setIntent(r.intent, r.info); } - logStartActivity(LOG_AM_NEW_INTENT, r, top.task); + logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); deliverNewIntentLocked(top, r.intent); } else { // A special case: we need to @@ -3463,7 +3433,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // desires. if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 && taskTop.realActivity.equals(r.realActivity)) { - logStartActivity(LOG_AM_NEW_INTENT, r, taskTop.task); + logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task); if (taskTop.frontOfTask) { taskTop.task.setIntent(r.intent, r.info); } @@ -3508,9 +3478,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen //String uri = r.intent.toURI(); //Intent intent2 = new Intent(uri); - //Log.i(TAG, "Given intent: " + r.intent); - //Log.i(TAG, "URI is: " + uri); - //Log.i(TAG, "To intent: " + intent2); + //Slog.i(TAG, "Given intent: " + r.intent); + //Slog.i(TAG, "URI is: " + uri); + //Slog.i(TAG, "To intent: " + intent2); if (r.packageName != null) { // If the activity being launched is the same as the one currently @@ -3523,7 +3493,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) { - logStartActivity(LOG_AM_NEW_INTENT, top, top.task); + logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task); // For paranoia, make sure we have correctly // resumed the top activity. if (doResume) { @@ -3563,7 +3533,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } r.task = new TaskRecord(mCurTask, r.info, intent, (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); - if (DEBUG_TASKS) Log.v(TAG, "Starting new activity " + r + if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " + r.task); newTask = true; addRecentTaskLocked(r.task); @@ -3577,7 +3547,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord top = performClearTaskLocked( sourceRecord.task.taskId, r, launchFlags, true); if (top != null) { - logStartActivity(LOG_AM_NEW_INTENT, r, top.task); + logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); deliverNewIntentLocked(top, r.intent); // For paranoia, make sure we have correctly // resumed the top activity. @@ -3594,7 +3564,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId); if (where >= 0) { HistoryRecord top = moveActivityToFrontLocked(where); - logStartActivity(LOG_AM_NEW_INTENT, r, top.task); + logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); deliverNewIntentLocked(top, r.intent); if (doResume) { resumeTopActivityLocked(null); @@ -3606,7 +3576,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // to keep the new one in the same task as the one that is starting // it. r.task = sourceRecord.task; - if (DEBUG_TASKS) Log.v(TAG, "Starting new activity " + r + if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in existing task " + r.task); } else { @@ -3620,22 +3590,49 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ? prev.task : new TaskRecord(mCurTask, r.info, intent, (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); - if (DEBUG_TASKS) Log.v(TAG, "Starting new activity " + r + if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new guessed " + r.task); } if (newTask) { - EventLog.writeEvent(LOG_AM_CREATE_TASK, r.task.taskId); + EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId); } - logStartActivity(LOG_AM_CREATE_ACTIVITY, r, r.task); + logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task); startActivityLocked(r, newTask, doResume); return START_SUCCESS; } - public final int startActivity(IApplicationThread caller, + void reportActivityLaunchedLocked(boolean timeout, HistoryRecord r, + long thisTime, long totalTime) { + for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) { + WaitResult w = mWaitingActivityLaunched.get(i); + w.timeout = timeout; + if (r != null) { + w.who = new ComponentName(r.info.packageName, r.info.name); + } + w.thisTime = thisTime; + w.totalTime = totalTime; + } + notify(); + } + + void reportActivityVisibleLocked(HistoryRecord r) { + for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) { + WaitResult w = mWaitingActivityVisible.get(i); + w.timeout = false; + if (r != null) { + w.who = new ComponentName(r.info.packageName, r.info.name); + } + w.totalTime = SystemClock.uptimeMillis() - w.thisTime; + w.thisTime = w.totalTime; + } + notify(); + } + + private final int startActivityMayWait(IApplicationThread caller, Intent intent, String resolvedType, Uri[] grantedUriPermissions, int grantedMode, IBinder resultTo, String resultWho, int requestCode, boolean onlyIfNeeded, - boolean debug) { + boolean debug, WaitResult outResult, Configuration config) { // Refuse possible leaked file descriptors if (intent != null && intent.hasFileDescriptors()) { throw new IllegalArgumentException("File descriptors passed in Intent"); @@ -3675,18 +3672,109 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - synchronized(this) { + synchronized (this) { + int callingPid; + int callingUid; + if (caller == null) { + callingPid = Binder.getCallingPid(); + callingUid = Binder.getCallingUid(); + } else { + callingPid = callingUid = -1; + } + + mConfigWillChange = config != null && mConfiguration.diff(config) != 0; + if (DEBUG_CONFIGURATION) Slog.v(TAG, + "Starting activity when config will change = " + mConfigWillChange); + final long origId = Binder.clearCallingIdentity(); + int res = startActivityLocked(caller, intent, resolvedType, grantedUriPermissions, grantedMode, aInfo, - resultTo, resultWho, requestCode, -1, -1, + resultTo, resultWho, requestCode, callingPid, callingUid, onlyIfNeeded, componentSpecified); + + if (mConfigWillChange) { + // If the caller also wants to switch to a new configuration, + // do so now. This allows a clean switch, as we are waiting + // for the current activity to pause (so we will not destroy + // it), and have not yet started the next activity. + enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, + "updateConfiguration()"); + mConfigWillChange = false; + if (DEBUG_CONFIGURATION) Slog.v(TAG, + "Updating to new configuration after starting activity."); + updateConfigurationLocked(config, null); + } + Binder.restoreCallingIdentity(origId); + + if (outResult != null) { + outResult.result = res; + if (res == IActivityManager.START_SUCCESS) { + mWaitingActivityLaunched.add(outResult); + do { + try { + wait(); + } catch (InterruptedException e) { + } + } while (!outResult.timeout && outResult.who == null); + } else if (res == IActivityManager.START_TASK_TO_FRONT) { + HistoryRecord r = this.topRunningActivityLocked(null); + if (r.nowVisible) { + outResult.timeout = false; + outResult.who = new ComponentName(r.info.packageName, r.info.name); + outResult.totalTime = 0; + outResult.thisTime = 0; + } else { + outResult.thisTime = SystemClock.uptimeMillis(); + mWaitingActivityVisible.add(outResult); + do { + try { + wait(); + } catch (InterruptedException e) { + } + } while (!outResult.timeout && outResult.who == null); + } + } + } + return res; } } - public int startActivityIntentSender(IApplicationThread caller, + public final int startActivity(IApplicationThread caller, + Intent intent, String resolvedType, Uri[] grantedUriPermissions, + int grantedMode, IBinder resultTo, + String resultWho, int requestCode, boolean onlyIfNeeded, + boolean debug) { + return startActivityMayWait(caller, intent, resolvedType, + grantedUriPermissions, grantedMode, resultTo, resultWho, + requestCode, onlyIfNeeded, debug, null, null); + } + + public final WaitResult startActivityAndWait(IApplicationThread caller, + Intent intent, String resolvedType, Uri[] grantedUriPermissions, + int grantedMode, IBinder resultTo, + String resultWho, int requestCode, boolean onlyIfNeeded, + boolean debug) { + WaitResult res = new WaitResult(); + startActivityMayWait(caller, intent, resolvedType, + grantedUriPermissions, grantedMode, resultTo, resultWho, + requestCode, onlyIfNeeded, debug, res, null); + return res; + } + + public final int startActivityWithConfig(IApplicationThread caller, + Intent intent, String resolvedType, Uri[] grantedUriPermissions, + int grantedMode, IBinder resultTo, + String resultWho, int requestCode, boolean onlyIfNeeded, + boolean debug, Configuration config) { + return startActivityMayWait(caller, intent, resolvedType, + grantedUriPermissions, grantedMode, resultTo, resultWho, + requestCode, onlyIfNeeded, debug, null, config); + } + + public int startActivityIntentSender(IApplicationThread caller, IntentSender intent, Intent fillInIntent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues) { @@ -3913,7 +4001,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private final void stopActivityLocked(HistoryRecord r) { - if (DEBUG_SWITCH) Log.d(TAG, "Stopping: " + r); + if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r); if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) { if (!r.finishing) { @@ -3928,7 +4016,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { r.stopped = false; r.state = ActivityState.STOPPING; - if (DEBUG_VISBILITY) Log.v( + if (DEBUG_VISBILITY) Slog.v( TAG, "Stopping visible=" + r.visible + " for " + r); if (!r.visible) { mWindowManager.setAppVisibility(r, false); @@ -3938,7 +4026,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Maybe just ignore exceptions here... if the process // has crashed, our death notification will clean things // up. - Log.w(TAG, "Exception thrown during pause", e); + Slog.w(TAG, "Exception thrown during pause", e); // Just in case, assume it to be stopped. r.stopped = true; r.state = ActivityState.STOPPED; @@ -3955,7 +4043,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ private final boolean requestFinishActivityLocked(IBinder token, int resultCode, Intent resultData, String reason) { - if (DEBUG_RESULTS) Log.v( + if (DEBUG_RESULTS) Slog.v( TAG, "Finishing activity: token=" + token + ", result=" + resultCode + ", data=" + resultData); @@ -3994,19 +4082,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final boolean finishActivityLocked(HistoryRecord r, int index, int resultCode, Intent resultData, String reason) { if (r.finishing) { - Log.w(TAG, "Duplicate finish request for " + r); + Slog.w(TAG, "Duplicate finish request for " + r); return false; } r.finishing = true; - EventLog.writeEvent(LOG_AM_FINISH_ACTIVITY, + EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, System.identityHashCode(r), r.task.taskId, r.shortComponentName, reason); r.task.numActivities--; - if (r.frontOfTask && index < (mHistory.size()-1)) { + if (index < (mHistory.size()-1)) { HistoryRecord next = (HistoryRecord)mHistory.get(index+1); if (next.task == r.task) { - next.frontOfTask = true; + if (r.frontOfTask) { + // The next activity is now the front of the task. + next.frontOfTask = true; + } + if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { + // If the caller asked that this activity (and all above it) + // be cleared when the task is reset, don't lose that information, + // but propagate it up to the next activity. + next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + } } } @@ -4018,7 +4115,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // send the result HistoryRecord resultTo = r.resultTo; if (resultTo != null) { - if (DEBUG_RESULTS) Log.v(TAG, "Adding result to " + resultTo + if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo + " who=" + r.resultWho + " req=" + r.requestCode + " res=" + resultCode + " data=" + resultData); if (r.info.applicationInfo.uid > 0) { @@ -4029,7 +4126,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen resultData); r.resultTo = null; } - else if (DEBUG_RESULTS) Log.v(TAG, "No result destination from " + r); + else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r); // Make sure this HistoryRecord is not holding on to other resources, // because clients have remote IPC references to this object so we @@ -4049,7 +4146,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mResumedActivity == r) { boolean endTask = index <= 0 || ((HistoryRecord)mHistory.get(index-1)).task != r.task; - if (DEBUG_TRANSITION) Log.v(TAG, + if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare close transition: finishing " + r); mWindowManager.prepareAppTransition(endTask ? WindowManagerPolicy.TRANSIT_TASK_CLOSE @@ -4059,19 +4156,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWindowManager.setAppVisibility(r, false); 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"); + if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r); + if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false"); startPausingLocked(false, false); } } else if (r.state != ActivityState.PAUSING) { // If the activity is PAUSING, we will complete the finish once // it is done pausing; else we can just directly finish it here. - if (DEBUG_PAUSE) Log.v(TAG, "Finish not pausing: " + r); + if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r); return finishCurrentActivityLocked(r, index, FINISH_AFTER_PAUSE) == null; } else { - if (DEBUG_PAUSE) Log.v(TAG, "Finish waiting for pause of: " + r); + if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r); } return false; @@ -4131,7 +4228,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } else { // Need to go through the full pause cycle to get this // activity into the stopped state and then finish it. - if (localLOGV) Log.v(TAG, "Enqueueing pending finish: " + r); + if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r); mFinishingActivities.add(r); resumeTopActivityLocked(null); } @@ -4187,7 +4284,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen data, r); } - if (DEBUG_RESULTS) Log.v(TAG, "Send activity result to " + r + if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r + " : who=" + resultWho + " req=" + requestCode + " res=" + resultCode + " data=" + data); if (mResumedActivity == r && r.app != null && r.app.thread != null) { @@ -4198,7 +4295,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); + Slog.w(TAG, "Exception thrown sending result to " + r, e); } } @@ -4232,6 +4329,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + public boolean willActivityBeVisible(IBinder token) { + synchronized(this) { + int i; + for (i=mHistory.size()-1; i>=0; i--) { + HistoryRecord r = (HistoryRecord)mHistory.get(i); + if (r == token) { + return true; + } + if (r.fullscreen && !r.finishing) { + return false; + } + } + return true; + } + } + public void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim) { synchronized(this) { @@ -4341,10 +4454,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ private final boolean destroyActivityLocked(HistoryRecord r, boolean removeFromApp) { - if (DEBUG_SWITCH) Log.v( + if (DEBUG_SWITCH) Slog.v( TAG, "Removing activity: token=" + r + ", app=" + (r.app != null ? r.app.processName : "(null)")); - EventLog.writeEvent(LOG_AM_DESTROY_ACTIVITY, + EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, System.identityHashCode(r), r.task.taskId, r.shortComponentName); @@ -4352,7 +4465,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen cleanUpActivityLocked(r, false); - if (r.app != null) { + final boolean hadApp = r.app != null; + + if (hadApp) { if (removeFromApp) { int idx = r.app.activities.indexOf(r); if (idx >= 0) { @@ -4361,19 +4476,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.persistent) { decPersistentCountLocked(r.app); } + if (r.app.activities.size() == 0) { + // No longer have activities, so update location in + // LRU list. + updateLruProcessLocked(r.app, true, false); + } } boolean skipDestroy = false; try { - if (DEBUG_SWITCH) Log.i(TAG, "Destroying: " + r); + if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r); r.app.thread.scheduleDestroyActivity(r, r.finishing, r.configChangeFlags); } catch (Exception e) { // We can just ignore exceptions here... if the process // has crashed, our death notification will clean things // up. - //Log.w(TAG, "Exception thrown during finish", e); + //Slog.w(TAG, "Exception thrown during finish", e); if (r.finishing) { removeActivityFromHistoryLocked(r); removedFromHistory = true; @@ -4404,27 +4524,25 @@ 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"); + if (!mLRUActivities.remove(r) && hadApp) { + Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list"); } return removedFromHistory; } - private static void removeHistoryRecordsForAppLocked(ArrayList list, - ProcessRecord app) - { + private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) { int i = list.size(); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Removing app " + app + " from list " + list + " with " + i + " entries"); while (i > 0) { i--; HistoryRecord r = (HistoryRecord)list.get(i); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Record #" + i + " " + r + ": app=" + r.app); if (r.app == app) { - if (localLOGV) Log.v(TAG, "Removing this entry!"); + if (localLOGV) Slog.v(TAG, "Removing this entry!"); list.remove(i); } } @@ -4439,12 +4557,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean restarting) { cleanUpApplicationRecordLocked(app, restarting, -1); if (!restarting) { - mLRUProcesses.remove(app); + mLruProcesses.remove(app); } // Just in case... if (mPausingActivity != null && mPausingActivity.app == app) { - if (DEBUG_PAUSE) Log.v(TAG, "App died while pausing: " + mPausingActivity); + if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " + mPausingActivity); mPausingActivity = null; } if (mLastPausedActivity != null && mLastPausedActivity.app == app) { @@ -4462,16 +4580,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Clean out the history list. int i = mHistory.size(); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Removing app " + app + " from history with " + i + " entries"); while (i > 0) { i--; HistoryRecord r = (HistoryRecord)mHistory.get(i); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Record #" + i + " " + r + ": app=" + r.app); if (r.app == app) { if ((!r.haveState && !r.stateNotNeeded) || r.finishing) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Removing this entry! frozen=" + r.haveState + " finishing=" + r.finishing); mHistory.remove(i); @@ -4486,7 +4604,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } else { // We have the current state for this activity, so // it can be restarted later when needed. - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Keeping entry, setting app to null"); if (r.visible) { hasVisibleActivities = true; @@ -4507,7 +4625,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.activities.clear(); if (app.instrumentationClass != null) { - Log.w(TAG, "Crash of app " + app.processName + Slog.w(TAG, "Crash of app " + app.processName + " running instrumentation " + app.instrumentationClass); Bundle info = new Bundle(); info.putString("shortMsg", "Process crashed."); @@ -4532,10 +4650,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen IBinder threadBinder = thread.asBinder(); // Find the application record. - int count = mLRUProcesses.size(); - int i; - for (i=0; i<count; i++) { - ProcessRecord rec = mLRUProcesses.get(i); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord rec = mLruProcesses.get(i); if (rec.thread != null && rec.thread.asBinder() == threadBinder) { return i; } @@ -4550,7 +4666,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } int appIndex = getLRURecordIndexForAppLocked(thread); - return appIndex >= 0 ? mLRUProcesses.get(appIndex) : null; + return appIndex >= 0 ? mLruProcesses.get(appIndex) : null; } private final void appDiedLocked(ProcessRecord app, int pid, @@ -4561,10 +4677,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Clean up already done if the process has been re-started. if (app.pid == pid && app.thread != null && app.thread.asBinder() == thread.asBinder()) { - Log.i(TAG, "Process " + app.processName + " (pid " + pid + Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died."); - EventLog.writeEvent(LOG_AM_PROCESS_DIED, app.pid, app.processName); - if (localLOGV) Log.v( + EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.pid, app.processName); + if (localLOGV) Slog.v( TAG, "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder()); boolean doLowMem = app.instrumentationClass == null; @@ -4575,10 +4691,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // and the app that died was not running instrumentation, // then tell everyone we are now low on memory. boolean haveBg = false; - int count = mLRUProcesses.size(); - int i; - for (i=0; i<count; i++) { - ProcessRecord rec = mLRUProcesses.get(i); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord rec = mLruProcesses.get(i); if (rec.thread != null && rec.setAdj >= HIDDEN_APP_MIN_ADJ) { haveBg = true; break; @@ -4586,11 +4700,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (!haveBg) { - Log.i(TAG, "Low Memory: No more background processes."); - EventLog.writeEvent(LOG_AM_LOW_MEMORY, mLRUProcesses.size()); + Slog.i(TAG, "Low Memory: No more background processes."); + EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size()); long now = SystemClock.uptimeMillis(); - for (i=0; i<count; i++) { - ProcessRecord rec = mLRUProcesses.get(i); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord rec = mLruProcesses.get(i); if (rec != app && rec.thread != null && (rec.lastLowMemory+GC_MIN_INTERVAL) <= now) { // The low memory report is overriding any current @@ -4612,211 +4726,184 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } else if (app.pid != pid) { // A new process has already been started. - Log.i(TAG, "Process " + app.processName + " (pid " + pid + Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died and restarted (pid " + app.pid + ")."); - EventLog.writeEvent(LOG_AM_PROCESS_DIED, pid, app.processName); - } else if (Config.LOGD) { - Log.d(TAG, "Received spurious death notification for thread " + EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.pid, app.processName); + } else if (DEBUG_PROCESSES) { + Slog.d(TAG, "Received spurious death notification for thread " + thread.asBinder()); } } - final String readFile(String filename) { - try { - FileInputStream fs = new FileInputStream(filename); - byte[] inp = new byte[8192]; - int size = fs.read(inp); - fs.close(); - return new String(inp, 0, 0, size); - } catch (java.io.IOException e) { + /** + * If a stack trace dump file is configured, dump process stack traces. + * @param pids of dalvik VM processes to dump stack traces for + * @return file containing stack traces, or null if no dump file is configured + */ + public static File dumpStackTraces(ArrayList<Integer> pids) { + String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null); + if (tracesPath == null || tracesPath.length() == 0) { + return null; } - return ""; - } - final void appNotRespondingLocked(ProcessRecord app, HistoryRecord activity, - HistoryRecord reportedActivity, final String annotation) { - if (app.notResponding || app.crashing) { - return; + File tracesFile = new File(tracesPath); + try { + File tracesDir = tracesFile.getParentFile(); + if (!tracesDir.exists()) tracesFile.mkdirs(); + FileUtils.setPermissions(tracesDir.getPath(), 0775, -1, -1); // drwxrwxr-x + + if (tracesFile.exists()) tracesFile.delete(); + tracesFile.createNewFile(); + FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw- + } catch (IOException e) { + Slog.w(TAG, "Unable to prepare ANR traces file: " + tracesPath, e); + return null; } - - // Log the ANR to the event log. - EventLog.writeEvent(LOG_ANR, app.pid, app.processName, annotation); - - // If we are on a secure build and the application is not interesting to the user (it is - // not visible or in the background), just kill it instead of displaying a dialog. - boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0")); - if (isSecure && !app.isInterestingToUserLocked() && Process.myPid() != app.pid) { - Process.killProcess(app.pid); - return; + + // Use a FileObserver to detect when traces finish writing. + // The order of traces is considered important to maintain for legibility. + FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) { + public synchronized void onEvent(int event, String path) { notify(); } + }; + + try { + observer.startWatching(); + int num = pids.size(); + for (int i = 0; i < num; i++) { + synchronized (observer) { + Process.sendSignal(pids.get(i), Process.SIGNAL_QUIT); + observer.wait(200); // Wait for write-close, give up after 200msec + } + } + } catch (InterruptedException e) { + Log.wtf(TAG, e); + } finally { + observer.stopWatching(); } + + return tracesFile; + } + + final void appNotResponding(ProcessRecord app, HistoryRecord activity, + HistoryRecord parent, final String annotation) { + ArrayList<Integer> pids = new ArrayList<Integer>(20); - // DeviceMonitor.start(); + synchronized (this) { + // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down. + if (mShuttingDown) { + Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation); + return; + } else if (app.notResponding) { + Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation); + return; + } else if (app.crashing) { + Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation); + return; + } + + // In case we come through here for the same app before completing + // this one, mark as anring now so we will bail out. + app.notResponding = true; - String processInfo = null; - if (MONITOR_CPU_USAGE) { - updateCpuStatsNow(); - synchronized (mProcessStatsThread) { - processInfo = mProcessStats.printCurrentState(); + // Log the ANR to the event log. + EventLog.writeEvent(EventLogTags.AM_ANR, app.pid, app.processName, app.info.flags, + annotation); + + // Dump thread traces as quickly as we can, starting with "interesting" processes. + pids.add(app.pid); + + int parentPid = app.pid; + if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid; + if (parentPid != app.pid) pids.add(parentPid); + + if (MY_PID != app.pid && MY_PID != parentPid) pids.add(MY_PID); + + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord r = mLruProcesses.get(i); + if (r != null && r.thread != null) { + int pid = r.pid; + if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) pids.add(pid); + } } } + File tracesFile = dumpStackTraces(pids); + + // Log the ANR to the main log. StringBuilder info = mStringBuilder; info.setLength(0); - info.append("ANR in process: "); - info.append(app.processName); - if (reportedActivity != null && reportedActivity.app != null) { - info.append(" (last in "); - info.append(reportedActivity.app.processName); - info.append(")"); + info.append("ANR in ").append(app.processName); + if (activity != null && activity.shortComponentName != null) { + info.append(" (").append(activity.shortComponentName).append(")"); } + info.append("\n"); if (annotation != null) { - info.append("\nAnnotation: "); - info.append(annotation); + info.append("Reason: ").append(annotation).append("\n"); + } + if (parent != null && parent != activity) { + info.append("Parent: ").append(parent.shortComponentName).append("\n"); } + + String cpuInfo = null; if (MONITOR_CPU_USAGE) { - info.append("\nCPU usage:\n"); - info.append(processInfo); + updateCpuStatsNow(); + synchronized (mProcessStatsThread) { + cpuInfo = mProcessStats.printCurrentState(); + } + info.append(cpuInfo); } - Log.i(TAG, info.toString()); - // The application is not responding. Dump as many thread traces as we can. - boolean fileDump = prepareTraceFile(true); - if (!fileDump) { - // Dumping traces to the log, just dump the process that isn't responding so - // we don't overflow the log + Slog.e(TAG, info.toString()); + if (tracesFile == null) { + // There is no trace file, so dump (only) the alleged culprit's threads to the log Process.sendSignal(app.pid, Process.SIGNAL_QUIT); - } else { - // Dumping traces to a file so dump all active processes we know about - synchronized (this) { - // First, these are the most important processes. - final int[] imppids = new int[3]; - int i=0; - imppids[0] = app.pid; - i++; - if (reportedActivity != null && reportedActivity.app != null - && reportedActivity.app.thread != null - && reportedActivity.app.pid != app.pid) { - imppids[i] = reportedActivity.app.pid; - i++; - } - imppids[i] = Process.myPid(); - for (i=0; i<imppids.length && imppids[i] != 0; i++) { - Process.sendSignal(imppids[i], Process.SIGNAL_QUIT); - synchronized (this) { - try { - wait(200); - } catch (InterruptedException e) { - } - } - } - for (i = mLRUProcesses.size() - 1 ; i >= 0 ; i--) { - ProcessRecord r = mLRUProcesses.get(i); - boolean done = false; - for (int j=0; j<imppids.length && imppids[j] != 0; j++) { - if (imppids[j] == r.pid) { - done = true; - break; - } - } - if (!done && r.thread != null) { - Process.sendSignal(r.pid, Process.SIGNAL_QUIT); - synchronized (this) { - try { - wait(200); - } catch (InterruptedException e) { - } - } - } - } - } } + addErrorToDropBox("anr", app, activity, parent, annotation, cpuInfo, tracesFile, null); + if (mController != null) { try { - int res = mController.appNotResponding(app.processName, - app.pid, info.toString()); + // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately + int res = mController.appNotResponding(app.processName, app.pid, info.toString()); if (res != 0) { - if (res < 0) { - // wait until the SIGQUIT has had a chance to process before killing the - // process. - try { - wait(2000); - } catch (InterruptedException e) { - } - - Process.killProcess(app.pid); - return; - } + if (res < 0 && app.pid != MY_PID) Process.killProcess(app.pid); + return; } } catch (RemoteException e) { mController = null; } } - makeAppNotRespondingLocked(app, - activity != null ? activity.shortComponentName : null, - annotation != null ? "ANR " + annotation : "ANR", - info.toString(), null); - Message msg = Message.obtain(); - HashMap map = new HashMap(); - msg.what = SHOW_NOT_RESPONDING_MSG; - msg.obj = map; - map.put("app", app); - if (activity != null) { - map.put("activity", activity); - } - - mHandler.sendMessage(msg); - return; - } - - /** - * If a stack trace file has been configured, prepare the filesystem - * by creating the directory if it doesn't exist and optionally - * removing the old trace file. - * - * @param removeExisting If set, the existing trace file will be removed. - * @return Returns true if the trace file preparations succeeded - */ - public static boolean prepareTraceFile(boolean removeExisting) { - String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null); - boolean fileReady = false; - if (!TextUtils.isEmpty(tracesPath)) { - File f = new File(tracesPath); - if (!f.exists()) { - // Ensure the enclosing directory exists - File dir = f.getParentFile(); - if (!dir.exists()) { - fileReady = dir.mkdirs(); - FileUtils.setPermissions(dir.getAbsolutePath(), - FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1); - } else if (dir.isDirectory()) { - fileReady = true; - } - } else if (removeExisting) { - // Remove the previous traces file, so we don't fill the disk. - // The VM will recreate it - Log.i(TAG, "Removing old ANR trace file from " + tracesPath); - fileReady = f.delete(); + // Unless configured otherwise, swallow ANRs in background processes & kill the process. + boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; + + synchronized (this) { + if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { + Process.killProcess(app.pid); + return; } - - if (removeExisting) { - try { - f.createNewFile(); - FileUtils.setPermissions(f.getAbsolutePath(), - FileUtils.S_IRWXU | FileUtils.S_IRWXG - | FileUtils.S_IWOTH | FileUtils.S_IROTH, -1, -1); - fileReady = true; - } catch (IOException e) { - Log.w(TAG, "Unable to make ANR traces file", e); - } + + // Set the app's notResponding state, and look up the errorReportReceiver + makeAppNotRespondingLocked(app, + activity != null ? activity.shortComponentName : null, + annotation != null ? "ANR " + annotation : "ANR", + info.toString()); + + // Bring up the infamous App Not Responding dialog + Message msg = Message.obtain(); + HashMap map = new HashMap(); + msg.what = SHOW_NOT_RESPONDING_MSG; + msg.obj = map; + map.put("app", app); + if (activity != null) { + map.put("activity", activity); } + + mHandler.sendMessage(msg); } - - return fileReady; } - private final void decPersistentCountLocked(ProcessRecord app) { app.persistentActivities--; @@ -4841,7 +4928,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + android.Manifest.permission.PERSISTENT_ACTIVITY; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } @@ -4853,26 +4940,26 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord r = (HistoryRecord)mHistory.get(index); ProcessRecord app = r.app; - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Setting persistence " + isPersistent + ": " + r); if (isPersistent) { if (r.persistent) { // Okay okay, I heard you already! - if (localLOGV) Log.v(TAG, "Already persistent!"); + if (localLOGV) Slog.v(TAG, "Already persistent!"); return; } r.persistent = true; app.persistentActivities++; - if (localLOGV) Log.v(TAG, "Num persistent now: " + app.persistentActivities); + if (localLOGV) Slog.v(TAG, "Num persistent now: " + app.persistentActivities); if (app.persistentActivities > 1) { // We aren't the first... - if (localLOGV) Log.v(TAG, "Not the first!"); + if (localLOGV) Slog.v(TAG, "Not the first!"); return; } if (app.persistent) { // This would be redundant. - if (localLOGV) Log.v(TAG, "App is persistent!"); + if (localLOGV) Slog.v(TAG, "App is persistent!"); return; } @@ -4910,14 +4997,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { } if (pkgUid == -1) { - Log.w(TAG, "Invalid packageName:" + packageName); + Slog.w(TAG, "Invalid packageName:" + packageName); return false; } if (uid == pkgUid || checkComponentPermission( android.Manifest.permission.CLEAR_APP_USER_DATA, pid, uid, -1) == PackageManager.PERMISSION_GRANTED) { - restartPackageLocked(packageName, pkgUid); + forceStopPackageLocked(packageName, pkgUid); } else { throw new SecurityException(pid+" does not have permission:"+ android.Manifest.permission.CLEAR_APP_USER_DATA+" to clear data" + @@ -4944,14 +5031,48 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return true; } - public void restartPackage(final String packageName) { - if (checkCallingPermission(android.Manifest.permission.RESTART_PACKAGES) + public void killBackgroundProcesses(final String packageName) { + if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) + != PackageManager.PERMISSION_GRANTED && + checkCallingPermission(android.Manifest.permission.RESTART_PACKAGES) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: killBackgroundProcesses() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + + long callingId = Binder.clearCallingIdentity(); + try { + IPackageManager pm = ActivityThread.getPackageManager(); + int pkgUid = -1; + synchronized(this) { + try { + pkgUid = pm.getPackageUid(packageName); + } catch (RemoteException e) { + } + if (pkgUid == -1) { + Slog.w(TAG, "Invalid packageName: " + packageName); + return; + } + killPackageProcessesLocked(packageName, pkgUid, + SECONDARY_SERVER_ADJ, false, true); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + public void forceStopPackage(final String packageName) { + if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) != PackageManager.PERMISSION_GRANTED) { - String msg = "Permission Denial: restartPackage() from pid=" + String msg = "Permission Denial: forceStopPackage() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " requires " + android.Manifest.permission.RESTART_PACKAGES; - Log.w(TAG, msg); + + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES; + Slog.w(TAG, msg); throw new SecurityException(msg); } @@ -4965,10 +5086,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { } if (pkgUid == -1) { - Log.w(TAG, "Invalid packageName: " + packageName); + Slog.w(TAG, "Invalid packageName: " + packageName); return; } - restartPackageLocked(packageName, pkgUid); + forceStopPackageLocked(packageName, pkgUid); } } finally { Binder.restoreCallingIdentity(callingId); @@ -4985,7 +5106,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // Make sure the uid is valid. if (uid < 0) { - Log.w(TAG, "Invalid uid specified for pkg : " + pkg); + Slog.w(TAG, "Invalid uid specified for pkg : " + pkg); return; } int callerUid = Binder.getCallingUid(); @@ -5068,7 +5189,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If the other end already died, then our work here is done. } } else { - Log.w(TAG, "Process/uid not found attempting kill of " + Slog.w(TAG, "Process/uid not found attempting kill of " + processName + " / " + uid); } } @@ -5078,8 +5199,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private void restartPackageLocked(final String packageName, int uid) { - uninstallPackageLocked(packageName, uid, false); + private void forceStopPackageLocked(final String packageName, int uid) { + forceStopPackageLocked(packageName, uid, false, false, true); Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, Uri.fromParts("package", packageName, null)); intent.putExtra(Intent.EXTRA_UID, uid); @@ -5088,59 +5209,77 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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(); - if (ba.get(uid) != null) { - badApps.remove(); - } - } - + private final boolean killPackageProcessesLocked(String packageName, int uid, + int minOomAdj, boolean callerWillRestart, boolean doit) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); // 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. + final String procNamePrefix = packageName + ":"; for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { final int NA = apps.size(); for (int ia=0; ia<NA; ia++) { ProcessRecord app = apps.valueAt(ia); if (app.removed) { - procs.add(app); + if (doit) { + procs.add(app); + } } else if ((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid) - || app.processName.equals(name) + || app.processName.equals(packageName) || app.processName.startsWith(procNamePrefix)) { - app.removed = true; - procs.add(app); + if (app.setAdj >= minOomAdj) { + if (!doit) { + return true; + } + app.removed = true; + procs.add(app); + } } } } - - N = procs.size(); - for (i=0; i<N; i++) { + + int N = procs.size(); + for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), callerWillRestart); } + return N > 0; + } + + private final boolean forceStopPackageLocked(String name, int uid, + boolean callerWillRestart, boolean purgeCache, boolean doit) { + int i, N; + + if (uid < 0) { + try { + uid = ActivityThread.getPackageManager().getPackageUid(name); + } catch (RemoteException e) { + } + } + + if (doit) { + Slog.i(TAG, "Force stopping package " + name + " uid=" + uid); + + Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator(); + while (badApps.hasNext()) { + SparseArray<Long> ba = badApps.next(); + if (ba.get(uid) != null) { + badApps.remove(); + } + } + } + + boolean didSomething = killPackageProcessesLocked(name, uid, -100, + callerWillRestart, doit); for (i=mHistory.size()-1; i>=0; i--) { HistoryRecord r = (HistoryRecord)mHistory.get(i); if (r.packageName.equals(name)) { - if (Config.LOGD) Log.d( - TAG, " Force finishing activity " - + r.intent.getComponent().flattenToShortString()); + if (!doit) { + return true; + } + didSomething = true; + Slog.i(TAG, " Force finishing activity " + r); if (r.app != null) { r.app.removed = true; } @@ -5152,6 +5291,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); for (ServiceRecord service : mServices.values()) { if (service.packageName.equals(name)) { + if (!doit) { + return true; + } + didSomething = true; + Slog.i(TAG, " Force stopping service " + service); if (service.app != null) { service.app.removed = true; } @@ -5165,13 +5309,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen bringDownServiceLocked(services.get(i), true); } - resumeTopActivityLocked(null); + if (doit) { + if (purgeCache) { + AttributeCache ac = AttributeCache.instance(); + if (ac != null) { + ac.removePackage(name); + } + } + resumeTopActivityLocked(null); + } + + return didSomething; } private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart) { final String name = app.processName; final int uid = app.info.uid; - if (Config.LOGD) Log.d( + if (DEBUG_PROCESSES) Slog.d( TAG, "Force removing process " + app + " (" + name + "/" + uid + ")"); @@ -5184,7 +5338,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } handleAppDiedLocked(app, true); - mLRUProcesses.remove(app); + mLruProcesses.remove(app); Process.killProcess(pid); if (app.persistent) { @@ -5213,8 +5367,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (gone) { - Log.w(TAG, "Process " + app + " failed to attach"); - EventLog.writeEvent(LOG_AM_PROCESS_START_TIMEOUT, pid, app.info.uid, + Slog.w(TAG, "Process " + app + " failed to attach"); + EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.info.uid, app.processName); mProcessNames.remove(app.processName, app.info.uid); // Take care of any launching providers waiting for this process. @@ -5224,7 +5378,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ServiceRecord sr = mPendingServices.get(i); if (app.info.uid == sr.appInfo.uid && app.processName.equals(sr.processName)) { - Log.w(TAG, "Forcing bringing down service: " + sr); + Slog.w(TAG, "Forcing bringing down service: " + sr); mPendingServices.remove(i); i--; bringDownServiceLocked(sr, true); @@ -5232,7 +5386,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } Process.killProcess(pid); if (mBackupTarget != null && mBackupTarget.app.pid == pid) { - Log.w(TAG, "Unattached app died before backup, skipping"); + Slog.w(TAG, "Unattached app died before backup, skipping"); try { IBackupManager bm = IBackupManager.Stub.asInterface( ServiceManager.getService(Context.BACKUP_SERVICE)); @@ -5242,12 +5396,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } if (mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid) { - Log.w(TAG, "Unattached app died before broadcast acknowledged, skipping"); + Slog.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); + Slog.w(TAG, "Spurious process start timeout - pid not known for " + app); } } @@ -5270,9 +5424,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (app == null) { - Log.w(TAG, "No pending application record for pid " + pid + Slog.w(TAG, "No pending application record for pid " + pid + " (IApplicationThread " + thread + "); dropping process"); - EventLog.writeEvent(LOG_AM_DROP_PROCESS, pid); + EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid); if (pid > 0 && pid != MY_PID) { Process.killProcess(pid); } else { @@ -5293,7 +5447,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Tell the process all about itself. - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Binding process pid " + pid + " to record " + app); String processName = app.processName; @@ -5306,11 +5460,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return false; } - EventLog.writeEvent(LOG_AM_PROCESS_BOUND, app.pid, app.processName); + EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.pid, app.processName); app.thread = thread; app.curAdj = app.setAdj = -100; - app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT; + app.curSchedGroup = Process.THREAD_GROUP_DEFAULT; + app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; app.forcingToForeground = null; app.foregroundServices = false; app.debugging = false; @@ -5321,10 +5476,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen List providers = normalMode ? generateApplicationProvidersLocked(app) : null; if (!normalMode) { - Log.i(TAG, "Launching preboot mode app: " + app); + Slog.i(TAG, "Launching preboot mode app: " + app); } - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "New app record " + app + " thread=" + thread.asBinder() + " pid=" + pid); try { @@ -5353,7 +5508,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (app.instrumentationClass != null) { ensurePackageDexOpt(app.instrumentationClass.getPackageName()); } - if (DEBUG_CONFIGURATION) Log.v(TAG, "Binding proc " + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Binding proc " + processName + " with config " + mConfiguration); thread.bindApplication(processName, app.instrumentationInfo != null ? app.instrumentationInfo : app.info, providers, @@ -5361,13 +5516,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.instrumentationArguments, app.instrumentationWatcher, testMode, isRestrictedBackupMode || !normalMode, mConfiguration, getCommonServicesLocked()); - updateLRUListLocked(app, false); + updateLruProcessLocked(app, false, true); app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis(); } catch (Exception e) { // todo: Yikes! What should we do? For now we will try to // start another process, but that could easily get us in // an infinite loop of restarting processes... - Log.w(TAG, "Exception thrown during bind!", e); + Slog.w(TAG, "Exception thrown during bind!", e); app.resetPackageList(); startProcessLocked(app, "bind fail", processName); @@ -5391,7 +5546,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen didSomething = true; } } catch (Exception e) { - Log.w(TAG, "Exception in new application when starting activity " + Slog.w(TAG, "Exception in new application when starting activity " + hr.intent.getComponent().flattenToShortString(), e); badApp = true; } @@ -5417,7 +5572,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen didSomething = true; } } catch (Exception e) { - Log.w(TAG, "Exception in new application when starting service " + Slog.w(TAG, "Exception in new application when starting service " + sr.shortName, e); badApp = true; } @@ -5431,7 +5586,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen processCurBroadcastLocked(br, app); didSomething = true; } catch (Exception e) { - Log.w(TAG, "Exception in new application when starting receiver " + Slog.w(TAG, "Exception in new application when starting receiver " + br.curComponent.flattenToShortString(), e); badApp = true; logBroadcastReceiverDiscard(br); @@ -5445,12 +5600,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Check whether the next backup agent is in this process... if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) { - if (DEBUG_BACKUP) Log.v(TAG, "New app is backup target, launching agent for " + app); + if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app); ensurePackageDexOpt(mBackupTarget.appInfo.packageName); try { thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode); } catch (Exception e) { - Log.w(TAG, "Exception scheduling backup agent creation: "); + Slog.w(TAG, "Exception scheduling backup agent creation: "); e.printStackTrace(); } } @@ -5496,7 +5651,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen && !mResumedActivity.waitingVisible; for (int i=0; i<N; i++) { HistoryRecord s = mStoppingActivities.get(i); - if (localLOGV) Log.v(TAG, "Stopping " + s + ": nowVisible=" + if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible + " waitingVisible=" + s.waitingVisible + " finishing=" + s.finishing); if (s.waitingVisible && nowVisible) { @@ -5508,12 +5663,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // so get rid of it. Otherwise, we need to go through the // normal flow and hide it once we determine that it is // hidden by the activities in front of it. - if (localLOGV) Log.v(TAG, "Before stopping, can hide: " + s); + if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s); mWindowManager.setAppVisibility(s, false); } } if (!s.waitingVisible && remove) { - if (localLOGV) Log.v(TAG, "Ready to stop: " + s); + if (localLOGV) Slog.v(TAG, "Ready to stop: " + s); if (stops == null) { stops = new ArrayList<HistoryRecord>(); } @@ -5528,14 +5683,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } void enableScreenAfterBoot() { - EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN, + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN, SystemClock.uptimeMillis()); mWindowManager.enableScreenAfterBoot(); } final void activityIdleInternal(IBinder token, boolean fromTimeout, Configuration config) { - if (localLOGV) Log.v(TAG, "Activity idle: " + token); + if (localLOGV) Slog.v(TAG, "Activity idle: " + token); ArrayList<HistoryRecord> stops = null; ArrayList<HistoryRecord> finishes = null; @@ -5557,6 +5712,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); + if (fromTimeout) { + reportActivityLaunchedLocked(fromTimeout, r, -1, -1); + } + // This is a hack to semi-deal with a race condition // in the client where it can be constructed with a // newer configuration from when we asked it to launch. @@ -5583,14 +5742,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If this activity is fullscreen, set up to hide those under it. - if (DEBUG_VISBILITY) Log.v(TAG, "Idle activity for " + r); + if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r); ensureActivitiesVisibleLocked(null, 0); - //Log.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout); + //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout); if (!mBooted && !fromTimeout) { mBooted = true; enableScreen = true; } + + } else if (fromTimeout) { + reportActivityLaunchedLocked(fromTimeout, null, -1, -1); } // Atomically retrieve all of the other things to do. @@ -5616,7 +5778,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { sendThumbnail.requestThumbnail(token); } catch (Exception e) { - Log.w(TAG, "Exception thrown when requesting thumbnail", e); + Slog.w(TAG, "Exception thrown when requesting thumbnail", e); sendPendingThumbnail(null, token, null, null, true); } } @@ -5663,21 +5825,38 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } final void finishBooting() { - // Ensure that any processes we had put on hold are now started - // up. - final int NP = mProcessesOnHold.size(); - if (NP > 0) { - ArrayList<ProcessRecord> procs = - new ArrayList<ProcessRecord>(mProcessesOnHold); - for (int ip=0; ip<NP; ip++) { - synchronized (this) { + IntentFilter pkgFilter = new IntentFilter(); + pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); + pkgFilter.addDataScheme("package"); + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); + if (pkgs != null) { + for (String pkg : pkgs) { + if (forceStopPackageLocked(pkg, -1, false, false, false)) { + setResultCode(Activity.RESULT_OK); + return; + } + } + } + } + }, pkgFilter); + + synchronized (this) { + // Ensure that any processes we had put on hold are now started + // up. + final int NP = mProcessesOnHold.size(); + if (NP > 0) { + ArrayList<ProcessRecord> procs = + new ArrayList<ProcessRecord>(mProcessesOnHold); + for (int ip=0; ip<NP; ip++) { this.startProcessLocked(procs.get(ip), "on-hold", null); } } - } - if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { - // Tell anyone interested that we are done booting! - synchronized (this) { + + if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { + // Tell anyone interested that we are done booting! broadcastIntentLocked(null, null, new Intent(Intent.ACTION_BOOT_COMPLETED, null), null, null, 0, null, null, @@ -5718,7 +5897,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } final void activityPaused(IBinder token, Bundle icicle, boolean timeout) { - if (DEBUG_PAUSE) Log.v( + if (DEBUG_PAUSE) Slog.v( TAG, "Activity paused: token=" + token + ", icicle=" + icicle + ", timeout=" + timeout); @@ -5737,7 +5916,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.state = ActivityState.PAUSED; completePauseLocked(); } else { - EventLog.writeEvent(LOG_AM_FAILED_TO_PAUSE_ACTIVITY, + EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, System.identityHashCode(r), r.shortComponentName, mPausingActivity != null ? mPausingActivity.shortComponentName : "(none)"); @@ -5748,7 +5927,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public final void activityStopped(IBinder token, Bitmap thumbnail, CharSequence description) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Activity stopped: token=" + token); HistoryRecord r = null; @@ -5782,7 +5961,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } public final void activityDestroyed(IBinder token) { - if (DEBUG_SWITCH) Log.v(TAG, "ACTIVITY DESTROYED: " + token); + if (DEBUG_SWITCH) Slog.v(TAG, "ACTIVITY DESTROYED: " + token); synchronized (this) { mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token); @@ -5873,7 +6052,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + ", uid=" + Binder.getCallingUid() + ", (need uid=" + uid + ")" + " is not allowed to send as package " + packageName; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } } @@ -5945,7 +6124,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + ", uid=" + Binder.getCallingUid() + " is not allowed to cancel packges " + rec.key.packageName; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } } catch (RemoteException e) { @@ -5967,12 +6146,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (!(pendingResult instanceof PendingIntentRecord)) { return null; } - synchronized(this) { - try { - PendingIntentRecord res = (PendingIntentRecord)pendingResult; - return res.key.packageName; - } catch (ClassCastException e) { - } + try { + PendingIntentRecord res = (PendingIntentRecord)pendingResult; + return res.key.packageName; + } catch (ClassCastException e) { } return null; } @@ -6016,7 +6193,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (mPidsSelfLocked) { ProcessRecord pr = mPidsSelfLocked.get(pid); if (pr == null) { - Log.w(TAG, "setProcessForeground called on unknown pid: " + pid); + Slog.w(TAG, "setProcessForeground called on unknown pid: " + pid); return; } ForegroundToken oldToken = mForegroundProcesses.get(pid); @@ -6078,7 +6255,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // client identity accordingly before proceeding. Identity tlsIdentity = sCallerIdentity.get(); if (tlsIdentity != null) { - Log.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {" + Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {" + tlsIdentity.pid + "," + tlsIdentity.uid + "}"); uid = tlsIdentity.uid; pid = tlsIdentity.pid; @@ -6091,7 +6268,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // If the target requires a specific UID, always fail for others. if (reqUid >= 0 && uid != reqUid) { - Log.w(TAG, "Permission denied: checkComponentPermission() reqUid=" + reqUid); + Slog.w(TAG, "Permission denied: checkComponentPermission() reqUid=" + reqUid); return PackageManager.PERMISSION_DENIED; } if (permission == null) { @@ -6102,7 +6279,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen .checkUidPermission(permission, uid); } catch (RemoteException e) { // Should never happen, but if it does... deny! - Log.e(TAG, "PackageManager is dead?!?", e); + Slog.e(TAG, "PackageManager is dead?!?", e); } return PackageManager.PERMISSION_DENIED; } @@ -6146,7 +6323,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + permission; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } @@ -6214,10 +6391,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Requested grant " + targetPkg + " permission to " + uri); + final IPackageManager pm = ActivityThread.getPackageManager(); // If this is not a content: uri, we can't do anything with it. if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Can't grant URI permission for non-content URI: " + uri); return; } @@ -6235,7 +6417,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } if (pi == null) { - Log.w(TAG, "No content provider found for: " + name); + Slog.w(TAG, "No content provider found for: " + name); return; } @@ -6243,6 +6425,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { targetUid = pm.getPackageUid(targetPkg); if (targetUid < 0) { + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Can't grant URI permission no uid for: " + targetPkg); return; } } catch (RemoteException ex) { @@ -6252,17 +6436,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // First... does the target actually need this permission? if (checkHoldingPermissionsLocked(pm, pi, targetUid, modeFlags)) { // No need to grant the target this permission. + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Target " + targetPkg + " already has full permission to " + uri); return; } - // Second... maybe someone else has already granted the - // permission? - if (checkUriPermissionLocked(uri, targetUid, modeFlags)) { - // No need to grant the target this permission. - return; - } - - // Third... is the provider allowing granting of URI permissions? + // Second... is the provider allowing granting of URI permissions? if (!pi.grantUriPermissions) { throw new SecurityException("Provider " + pi.packageName + "/" + pi.name @@ -6287,7 +6466,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - // Fourth... does the caller itself have permission to access + // Third... does the caller itself have permission to access // this uri? if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) { if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) { @@ -6300,6 +6479,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // to the uri, and the target doesn't. Let's now give this to // the target. + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Granting " + targetPkg + " permission to " + uri); + HashMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); if (targetUris == null) { @@ -6354,11 +6536,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " when granting permission to uri " + uri); } if (targetPkg == null) { - Log.w(TAG, "grantUriPermission: null target"); + Slog.w(TAG, "grantUriPermission: null target"); return; } if (uri == null) { - Log.w(TAG, "grantUriPermission: null uri"); + Slog.w(TAG, "grantUriPermission: null uri"); return; } @@ -6373,6 +6555,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(perm.uid); if (perms != null) { + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Removing " + perm.uid + " permission to " + perm.uri); perms.remove(perm.uri); if (perms.size() == 0) { mGrantedUriPermissions.remove(perm.uid); @@ -6412,6 +6596,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Revoking all granted permissions to " + uri); + final IPackageManager pm = ActivityThread.getPackageManager(); final String authority = uri.getAuthority(); @@ -6428,7 +6615,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } if (pi == null) { - Log.w(TAG, "No content provider found for: " + authority); + Slog.w(TAG, "No content provider found for: " + authority); return; } @@ -6470,6 +6657,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen continue toploop; } } + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Revoking " + perm.uid + " permission to " + perm.uri); perm.clearModes(modeFlags); if (perm.modeFlags == 0) { it.remove(); @@ -6495,7 +6684,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " when revoking permission to uri " + uri); } if (uri == null) { - Log.w(TAG, "revokeUriPermission: null uri"); + Slog.w(TAG, "revokeUriPermission: null uri"); return; } @@ -6521,7 +6710,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } if (pi == null) { - Log.w(TAG, "No content provider found for: " + authority); + Slog.w(TAG, "No content provider found for: " + authority); return; } @@ -6563,7 +6752,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord topRecord = null; synchronized(this) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "getTasks: max=" + maxNum + ", flags=" + flags + ", receiver=" + receiver); @@ -6581,7 +6770,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + android.Manifest.permission.GET_TASKS; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } @@ -6617,7 +6806,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen topDescription = r.description; } - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, r.intent.getComponent().flattenToShortString() + ": task=" + r.task); @@ -6636,7 +6825,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen //System.out.println( // "#" + maxNum + ": " + " descr=" + ci.description); if (ci.thumbnail == null && receiver != null) { - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "State=" + top.state + "Idle=" + top.idle + " app=" + top.app + " thr=" + (top.app != null ? top.app.thread : null)); @@ -6666,14 +6855,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - if (localLOGV) Log.v(TAG, "We have pending thumbnails: " + pending); + if (localLOGV) Slog.v(TAG, "We have pending thumbnails: " + pending); if (topThumbnail != null) { - if (localLOGV) Log.v(TAG, "Requesting top thumbnail"); + if (localLOGV) Slog.v(TAG, "Requesting top thumbnail"); try { topThumbnail.requestThumbnail(topRecord); } catch (Exception e) { - Log.w(TAG, "Exception thrown when requesting thumbnail", e); + Slog.w(TAG, "Exception thrown when requesting thumbnail", e); sendPendingThumbnail(null, topRecord, null, null, true); } } @@ -6698,6 +6887,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen enforceCallingPermission(android.Manifest.permission.GET_TASKS, "getRecentTasks()"); + IPackageManager pm = ActivityThread.getPackageManager(); + final int N = mRecentTasks.size(); ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<ActivityManager.RecentTaskInfo>( @@ -6714,6 +6905,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen rti.baseIntent = new Intent( tr.intent != null ? tr.intent : tr.affinityIntent); rti.origActivity = tr.origActivity; + + if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) { + // Check whether this activity is currently available. + try { + if (rti.origActivity != null) { + if (pm.getActivityInfo(rti.origActivity, 0) == null) { + continue; + } + } else if (rti.baseIntent != null) { + if (pm.queryIntentActivities(rti.baseIntent, + null, 0) == null) { + continue; + } + } + } catch (RemoteException e) { + // Will never happen. + } + } + res.add(rti); maxNum--; } @@ -6846,7 +7056,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // same task affinity as the one we are moving, // then merge it into the same task. target.task = p.task; - if (DEBUG_TASKS) Log.v(TAG, "Start pushing activity " + target + if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target + " out to bottom task " + p.task); } else { mCurTask++; @@ -6856,7 +7066,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen target.task = new TaskRecord(mCurTask, target.info, null, (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); target.task.affinityIntent = target.intent; - if (DEBUG_TASKS) Log.v(TAG, "Start pushing activity " + target + if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target + " out to new task " + target.task); } mWindowManager.setAppGroupId(target, task.taskId); @@ -6869,7 +7079,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (p.finishing) { continue; } - if (DEBUG_TASKS) Log.v(TAG, "Pushing next activity " + p + if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p + " out to target's task " + target.task); task.numActivities--; p.task = target.task; @@ -7006,7 +7216,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen p.task.numActivities--; p.task = task; mHistory.add(lastReparentPos, p); - if (DEBUG_TASKS) Log.v(TAG, "Pulling activity " + p + if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p + " in to resetting task " + task); task.numActivities++; mWindowManager.moveAppToken(lastReparentPos, p); @@ -7082,7 +7292,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private final void moveTaskToFrontLocked(TaskRecord tr, HistoryRecord reason) { - if (DEBUG_SWITCH) Log.v(TAG, "moveTaskToFront: " + tr); + if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr); final int task = tr.taskId; int top = mHistory.size()-1; @@ -7103,11 +7313,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // of the stack, keeping them in the same internal order. while (pos >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(pos); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "At " + pos + " ckp " + r.task + ": " + r); boolean first = true; if (r.task.taskId == task) { - if (localLOGV) Log.v(TAG, "Removing and adding at " + top); + if (localLOGV) Slog.v(TAG, "Removing and adding at " + top); mHistory.remove(pos); mHistory.add(top, r); moved.add(0, r); @@ -7120,7 +7330,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen pos--; } - if (DEBUG_TRANSITION) Log.v(TAG, + if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr); if (reason != null && (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { @@ -7139,7 +7349,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } finishTaskMoveLocked(task); - EventLog.writeEvent(LOG_TASK_TO_FRONT, task); + EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task); } private final void finishTaskMoveLocked(int task) { @@ -7196,7 +7406,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * @return Returns true if the move completed, false if not. */ private final boolean moveTaskToBackLocked(int task, HistoryRecord reason) { - Log.i(TAG, "moveTaskToBack: " + task); + Slog.i(TAG, "moveTaskToBack: " + task); // If we have a watcher, preflight the move before committing to it. First check // for *other* available tasks, but if none are available, then try again allowing the @@ -7222,7 +7432,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ArrayList moved = new ArrayList(); - if (DEBUG_TRANSITION) Log.v(TAG, + if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to back transition: task=" + task); final int N = mHistory.size(); @@ -7233,10 +7443,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // of the stack, keeping them in the same internal order. while (pos < N) { HistoryRecord r = (HistoryRecord)mHistory.get(pos); - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "At " + pos + " ckp " + r.task + ": " + r); if (r.task.taskId == task) { - if (localLOGV) Log.v(TAG, "Removing and adding at " + (N-1)); + if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1)); mHistory.remove(pos); mHistory.add(bottom, r); moved.add(r); @@ -7280,7 +7490,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private final void moveTaskBackwardsLocked(int task) { - Log.e(TAG, "moveTaskBackwards not yet implemented!"); + Slog.e(TAG, "moveTaskBackwards not yet implemented!"); } public int getTaskForActivity(IBinder token, boolean onlyRoot) { @@ -7324,25 +7534,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (!r.finishing && r.task != cp && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) { cp = r.task; - //Log.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString() + //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString() // + "/aff=" + r.task.affinity + " to new cls=" // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity); if (r.task.affinity != null) { if (r.task.affinity.equals(info.taskAffinity)) { - //Log.i(TAG, "Found matching affinity!"); + //Slog.i(TAG, "Found matching affinity!"); return r; } } else if (r.task.intent != null && r.task.intent.getComponent().equals(cls)) { - //Log.i(TAG, "Found matching class!"); + //Slog.i(TAG, "Found matching class!"); //dump(); - //Log.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); + //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); return r; } else if (r.task.affinityIntent != null && r.task.affinityIntent.getComponent().equals(cls)) { - //Log.i(TAG, "Found matching class!"); + //Slog.i(TAG, "Found matching class!"); //dump(); - //Log.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); + //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); return r; } } @@ -7367,9 +7577,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord r = (HistoryRecord)mHistory.get(i); if (!r.finishing) { if (r.intent.getComponent().equals(cls)) { - //Log.i(TAG, "Found matching class!"); + //Slog.i(TAG, "Found matching class!"); //dump(); - //Log.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); + //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); return r; } } @@ -7474,7 +7684,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen pr.receiver.finished(); } } catch (Exception e) { - Log.w(TAG, "Exception thrown when sending thumbnail", e); + Slog.w(TAG, "Exception thrown when sending thumbnail", e); } } } @@ -7551,7 +7761,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid + ", uid=" + callingUid + ") requires " + cpi.readPermission + " or " + cpi.writePermission; - Log.w(TAG, msg); + Slog.w(TAG, msg); return msg; } @@ -7600,7 +7810,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // In this case the provider instance already exists, so we can // return it right away. if (r != null) { - if (DEBUG_PROVIDER) Log.v(TAG, + if (DEBUG_PROVIDER) Slog.v(TAG, "Adding provider requested by " + r.processName + " from process " + cpr.info.processName); @@ -7638,6 +7848,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ? cpi.readPermission : cpi.writePermission); } + if (!mSystemReady && !mDidUpdate && !mWaitingUpdate + && !cpi.processName.equals("system")) { + // If this content provider does not run in the system + // process, and the system is not yet ready to run other + // processes, then fail fast instead of hanging. + throw new IllegalArgumentException( + "Attempt to launch content provider before system ready"); + } + cpr = (ContentProviderRecord)mProvidersByClass.get(cpi.name); final boolean firstClass = cpr == null; if (firstClass) { @@ -7648,7 +7867,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen cpi.applicationInfo.packageName, STOCK_PM_FLAGS); if (ai == null) { - Log.w(TAG, "No package info for content provider " + Slog.w(TAG, "No package info for content provider " + cpi.name); return null; } @@ -7668,7 +7887,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (DEBUG_PROVIDER) { RuntimeException e = new RuntimeException("here"); - Log.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid + Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid + " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e); } @@ -7692,7 +7911,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen new ComponentName(cpi.applicationInfo.packageName, cpi.name), false); if (proc == null) { - Log.w(TAG, "Unable to launch app " + Slog.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " + name + ": process is bad"); @@ -7711,7 +7930,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mProvidersByName.put(name, cpr); if (r != null) { - if (DEBUG_PROVIDER) Log.v(TAG, + if (DEBUG_PROVIDER) Slog.v(TAG, "Adding provider requested by " + r.processName + " from process " + cpr.info.processName); @@ -7732,11 +7951,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (cpr) { while (cpr.provider == null) { if (cpr.launchingApp == null) { - Log.w(TAG, "Unable to launch app " + Slog.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " + name + ": launching app became null"); - EventLog.writeEvent(LOG_AM_PROVIDER_LOST_PROCESS, + EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS, cpi.applicationInfo.packageName, cpi.applicationInfo.uid, name); return null; @@ -7755,7 +7974,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (caller == null) { String msg = "null IApplicationThread when getting content provider " + name; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } @@ -7775,7 +7994,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name); if(cpr == null) { // remove from mProvidersByClass - if (DEBUG_PROVIDER) Log.v(TAG, name + + if (DEBUG_PROVIDER) Slog.v(TAG, name + " provider not found in providers list"); return; } @@ -7788,12 +8007,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen //update content provider record entry info ContentProviderRecord localCpr = (ContentProviderRecord) mProvidersByClass.get(cpr.info.name); - if (DEBUG_PROVIDER) Log.v(TAG, "Removing provider requested by " + if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by " + r.info.processName + " from process " + localCpr.appInfo.processName); if (localCpr.app == r) { //should not happen. taken care of as a local provider - Log.w(TAG, "removeContentProvider called on local provider: " + Slog.w(TAG, "removeContentProvider called on local provider: " + cpr.info.name + " in process " + r.processName); return; } else { @@ -7814,7 +8033,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name); if(cpr == null) { //remove from mProvidersByClass - if(localLOGV) Log.v(TAG, name+" content provider not found in providers list"); + if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list"); return; } @@ -7822,7 +8041,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ContentProviderRecord localCpr = (ContentProviderRecord) mProvidersByClass.get(cpr.info.name); localCpr.externals--; if (localCpr.externals < 0) { - Log.e(TAG, "Externals < 0 for content provider " + localCpr); + Slog.e(TAG, "Externals < 0 for content provider " + localCpr); } updateOomAdjLocked(); } @@ -7883,10 +8102,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } public static final void installSystemProviders() { - List providers = null; + List providers; synchronized (mSelf) { ProcessRecord app = mSelf.mProcessNames.get("system", Process.SYSTEM_UID); providers = mSelf.generateApplicationProvidersLocked(app); + if (providers != null) { + for (int i=providers.size()-1; i>=0; i--) { + ProviderInfo pi = (ProviderInfo)providers.get(i); + if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) { + Slog.w(TAG, "Not installing system proc provider " + pi.name + + ": not system .apk"); + providers.remove(i); + } + } + } } if (providers != null) { mSystemThread.installSystemProviders(providers); @@ -7914,7 +8143,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (app == null) { app = newProcessRecordLocked(null, info, null); mProcessNames.put(info.processName, info.uid, app); - updateLRUListLocked(app, true); + updateLruProcessLocked(app, true, true); } if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) @@ -7936,7 +8165,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized(this) { int count = mHistory.size(); - if (Config.LOGD) Log.d( + if (DEBUG_SWITCH) Slog.d( TAG, "Performing unhandledBack(): stack size = " + count); if (count > 1) { final long origId = Binder.clearCallingIdentity(); @@ -7973,7 +8202,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // We've got the fd now, so we're done with the provider. removeContentProviderExternal(name); } else { - Log.d(TAG, "Failed to get provider for authority '" + name + "'"); + Slog.d(TAG, "Failed to get provider for authority '" + name + "'"); } return pfd; } @@ -7986,7 +8215,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mResumedActivity != null) { pauseIfSleepingLocked(); } else { - Log.w(TAG, "goingToSleep with no resumed activity!"); + Slog.w(TAG, "goingToSleep with no resumed activity!"); } } } @@ -8010,7 +8239,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen while (mResumedActivity != null || mPausingActivity != null) { long delay = endTime - System.currentTimeMillis(); if (delay <= 0) { - Log.w(TAG, "Activity manager shutdown timed out"); + Slog.w(TAG, "Activity manager shutdown timed out"); timedout = true; break; } @@ -8042,8 +8271,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // one to pause. If we are pausing one, we will just let that stuff // run and release the wake lock when all done. if (mPausingActivity == null) { - if (DEBUG_PAUSE) Log.v(TAG, "Sleep needs to pause..."); - if (DEBUG_USER_LEAVING) Log.v(TAG, "Sleep => pause with userLeaving=false"); + if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause..."); + if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false"); startPausingLocked(false, true); } } @@ -8105,7 +8334,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return true; } - Log.w(TAG, name + " request from " + callingUid + " stopped"); + Slog.w(TAG, name + " request from " + callingUid + " stopped"); return false; } @@ -8137,7 +8366,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mDebugTransient = !persistent; if (packageName != null) { final long origId = Binder.clearCallingIdentity(); - uninstallPackageLocked(packageName, -1, false); + forceStopPackageLocked(packageName, -1, false, false, true); Binder.restoreCallingIdentity(origId); } } @@ -8164,6 +8393,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + public boolean isUserAMonkey() { + // For now the fact that there is a controller implies + // we have a monkey. + synchronized (this) { + return mController != null; + } + } + public void registerActivityWatcher(IActivityWatcher watcher) { synchronized (this) { mWatchers.register(watcher); @@ -8220,11 +8457,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - public boolean killPidsForMemory(int[] pids) { + public boolean killPids(int[] pids, String pReason) { if (Binder.getCallingUid() != Process.SYSTEM_UID) { - throw new SecurityException("killPidsForMemory only available to the system"); + throw new SecurityException("killPids only available to the system"); } - + String reason = (pReason == null) ? "Unknown" : pReason; // XXX Note: don't acquire main activity lock here, because the window // manager calls in with its locks held. @@ -8248,7 +8485,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (worstType < EMPTY_APP_ADJ && worstType > HIDDEN_APP_MIN_ADJ) { worstType = HIDDEN_APP_MIN_ADJ; } - Log.w(TAG, "Killing processes for memory at adjustment " + worstType); + Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType); for (int i=0; i<pids.length; i++) { ProcessRecord proc = mPidsSelfLocked.get(pids[i]); if (proc == null) { @@ -8256,10 +8493,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } int adj = proc.setAdj; if (adj >= worstType) { - Log.w(TAG, "Killing for memory: " + proc + " (adj " - + adj + ")"); - EventLog.writeEvent(LOG_AM_KILL_FOR_MEMORY, proc.pid, - proc.processName, adj); + Slog.w(TAG, "Killing " + proc + " (adj " + adj + "): " + reason); + EventLog.writeEvent(EventLogTags.AM_KILL, proc.pid, + proc.processName, adj, reason); killed = true; Process.killProcess(pids[i]); } @@ -8295,8 +8531,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (this) { mRequestPssCallback = completeCallback; mRequestPssList.clear(); - for (int i=mLRUProcesses.size()-1; i>=0; i--) { - ProcessRecord proc = mLRUProcesses.get(i); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord proc = mLruProcesses.get(i); if (!proc.persistent) { mRequestPssList.add(proc); } @@ -8356,25 +8592,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen i++; } - for (i=mLRUProcesses.size()-1; i>=0; i--) { - ProcessRecord proc = mLRUProcesses.get(i); + for (i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord proc = mLruProcesses.get(i); if (proc.persistent) { continue; } - //Log.i(TAG, "Proc " + proc + ": pss=" + proc.lastPss); + //Slog.i(TAG, "Proc " + proc + ": pss=" + proc.lastPss); if (proc.lastPss == 0) { stats.mNoPssCount++; continue; } - if (proc.setAdj == EMPTY_APP_ADJ) { - stats.mEmptyPss += proc.lastPss; - stats.mEmptyCount++; - } else if (proc.setAdj == CONTENT_PROVIDER_ADJ) { - stats.mEmptyPss += proc.lastPss; - stats.mEmptyCount++; - } else if (proc.setAdj >= HIDDEN_APP_MIN_ADJ) { - stats.mBackgroundPss += proc.lastPss; - stats.mBackgroundCount++; + if (proc.setAdj >= HIDDEN_APP_MIN_ADJ) { + if (proc.empty) { + stats.mEmptyPss += proc.lastPss; + stats.mEmptyCount++; + } else { + stats.mBackgroundPss += proc.lastPss; + stats.mBackgroundCount++; + } } else if (proc.setAdj >= VISIBLE_APP_ADJ) { stats.mVisiblePss += proc.lastPss; stats.mVisibleCount++; @@ -8424,7 +8659,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // This happens before any activities are started, so we can // change mConfiguration in-place. mConfiguration.updateFrom(configuration); - if (DEBUG_CONFIGURATION) Log.v(TAG, "Initial config: " + mConfiguration); + mConfigurationSeq = mConfiguration.seq = 1; + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Initial config: " + mConfiguration); } } @@ -8471,7 +8707,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ActivityInfo ai = ris.get(i).activityInfo; intent.setComponent(new ComponentName(ai.packageName, ai.name)); IIntentReceiver finisher = null; - if (i == 0) { + if (i == ris.size()-1) { finisher = new IIntentReceiver.Stub() { public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, @@ -8484,10 +8720,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } }; } - Log.i(TAG, "Sending system update to: " + intent.getComponent()); + Slog.i(TAG, "Sending system update to: " + intent.getComponent()); broadcastIntentLocked(null, null, intent, null, finisher, 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID); - if (i == 0) { + if (finisher != null) { mWaitingUpdate = true; } } @@ -8521,14 +8757,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized(this) { for (int i=procsToKill.size()-1; i>=0; i--) { ProcessRecord proc = procsToKill.get(i); - Log.i(TAG, "Removing system update proc: " + proc); + Slog.i(TAG, "Removing system update proc: " + proc); removeProcessLocked(proc, true); } } } - Log.i(TAG, "System now ready"); - EventLog.writeEvent(LOG_BOOT_PROGRESS_AMS_READY, + Slog.i(TAG, "System now ready"); + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_AMS_READY, SystemClock.uptimeMillis()); synchronized(this) { @@ -8609,89 +8845,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - boolean makeAppCrashingLocked(ProcessRecord app, - String tag, String shortMsg, String longMsg, byte[] crashData) { + private boolean makeAppCrashingLocked(ProcessRecord app, + String shortMsg, String longMsg, String stackTrace) { app.crashing = true; - app.crashingReport = generateProcessError(app, - ActivityManager.ProcessErrorStateInfo.CRASHED, tag, shortMsg, longMsg, crashData); + app.crashingReport = generateProcessError(app, + ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace); startAppProblemLocked(app); app.stopFreezingAllLocked(); return handleAppCrashLocked(app); } - private ComponentName getErrorReportReceiver(ProcessRecord app) { - // check if error reporting is enabled in Gservices - int enabled = Settings.Gservices.getInt(mContext.getContentResolver(), - Settings.Gservices.SEND_ACTION_APP_ERROR, 0); - if (enabled == 0) { - return null; - } - - IPackageManager pm = ActivityThread.getPackageManager(); - - try { - // look for receiver in the installer package - String candidate = pm.getInstallerPackageName(app.info.packageName); - ComponentName result = getErrorReportReceiver(pm, app.info.packageName, candidate); - if (result != null) { - return result; - } - - // if the error app is on the system image, look for system apps - // error receiver - if ((app.info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { - candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY); - result = getErrorReportReceiver(pm, app.info.packageName, candidate); - if (result != null) { - return result; - } - } - - // if there is a default receiver, try that - candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY); - return getErrorReportReceiver(pm, app.info.packageName, candidate); - } catch (RemoteException e) { - // should not happen - Log.e(TAG, "error talking to PackageManager", e); - return null; - } - } - - /** - * Return activity in receiverPackage that handles ACTION_APP_ERROR. - * - * @param pm PackageManager isntance - * @param errorPackage package which caused the error - * @param receiverPackage candidate package to receive the error - * @return activity component within receiverPackage which handles - * ACTION_APP_ERROR, or null if not found - */ - private ComponentName getErrorReportReceiver(IPackageManager pm, String errorPackage, - String receiverPackage) throws RemoteException { - if (receiverPackage == null || receiverPackage.length() == 0) { - return null; - } - - // break the loop if it's the error report receiver package that crashed - if (receiverPackage.equals(errorPackage)) { - return null; - } - - Intent intent = new Intent(Intent.ACTION_APP_ERROR); - intent.setPackage(receiverPackage); - ResolveInfo info = pm.resolveIntent(intent, null, 0); - if (info == null || info.activityInfo == null) { - return null; - } - return new ComponentName(receiverPackage, info.activityInfo.name); - } - - void makeAppNotRespondingLocked(ProcessRecord app, - String tag, String shortMsg, String longMsg, byte[] crashData) { + private void makeAppNotRespondingLocked(ProcessRecord app, + String activity, String shortMsg, String longMsg) { app.notResponding = true; - app.notRespondingReport = generateProcessError(app, - ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, tag, shortMsg, longMsg, - crashData); + app.notRespondingReport = generateProcessError(app, + ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, + activity, shortMsg, longMsg, null); startAppProblemLocked(app); app.stopFreezingAllLocked(); } @@ -8702,31 +8871,30 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * @param app The ProcessRecord in which the error occurred. * @param condition Crashing, Application Not Responding, etc. Values are defined in * ActivityManager.AppErrorStateInfo - * @param tag The tag that was passed into handleApplicationError(). Typically the classname. + * @param activity The activity associated with the crash, if known. * @param shortMsg Short message describing the crash. * @param longMsg Long message describing the crash. - * @param crashData Raw data passed into handleApplicationError(). Typically a stack trace. - * + * @param stackTrace Full crash stack trace, may be null. + * * @return Returns a fully-formed AppErrorStateInfo record. */ private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app, - int condition, String tag, String shortMsg, String longMsg, byte[] crashData) { + int condition, String activity, String shortMsg, String longMsg, String stackTrace) { ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo(); - + report.condition = condition; report.processName = app.processName; report.pid = app.pid; report.uid = app.info.uid; - report.tag = tag; + report.tag = activity; report.shortMsg = shortMsg; report.longMsg = longMsg; - report.crashData = crashData; + report.stackTrace = stackTrace; return report; } - void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog, - boolean crashed) { + void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog) { synchronized (this) { app.crashing = false; app.crashingReport = null; @@ -8739,35 +8907,32 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.waitDialog = null; } if (app.pid > 0 && app.pid != MY_PID) { - if (crashed) { - handleAppCrashLocked(app); - } - Log.i(ActivityManagerService.TAG, "Killing process " - + app.processName - + " (pid=" + app.pid + ") at user's request"); + handleAppCrashLocked(app); + Slog.i(ActivityManagerService.TAG, "Killing " + + app.processName + " (pid=" + app.pid + "): user's request"); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "user's request after error"); Process.killProcess(app.pid); } - } } - - boolean handleAppCrashLocked(ProcessRecord app) { + + private boolean handleAppCrashLocked(ProcessRecord app) { long now = SystemClock.uptimeMillis(); Long crashTime = mProcessCrashTimes.get(app.info.processName, app.info.uid); if (crashTime != null && now < crashTime+MIN_CRASH_INTERVAL) { // This process loses! - Log.w(TAG, "Process " + app.info.processName + Slog.w(TAG, "Process " + app.info.processName + " has crashed too many times: killing!"); - EventLog.writeEvent(LOG_AM_PROCESS_CRASHED_TOO_MUCH, + EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH, app.info.processName, app.info.uid); killServicesLocked(app, false); for (int i=mHistory.size()-1; i>=0; i--) { HistoryRecord r = (HistoryRecord)mHistory.get(i); if (r.app == app) { - if (Config.LOGD) Log.d( - TAG, " Force finishing activity " + Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed"); } @@ -8777,7 +8942,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // explicitly does so... but for persistent process, we really // need to keep it running. If a persistent process is actually // repeatedly crashing, then badness for everyone. - EventLog.writeEvent(LOG_AM_PROCESS_BAD, app.info.uid, + EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.info.uid, app.info.processName); mBadProcesses.put(app.info.processName, app.info.uid, now); app.bad = true; @@ -8786,6 +8951,34 @@ public final class ActivityManagerService extends ActivityManagerNative implemen removeProcessLocked(app, false); return false; } + } else { + HistoryRecord r = topRunningActivityLocked(null); + if (r.app == app) { + // If the top running activity is from this crashing + // process, then terminate it to avoid getting in a loop. + Slog.w(TAG, " Force finishing activity " + + r.intent.getComponent().flattenToShortString()); + int index = indexOfTokenLocked(r); + finishActivityLocked(r, index, + Activity.RESULT_CANCELED, null, "crashed"); + // Also terminate an activities below it that aren't yet + // stopped, to avoid a situation where one will get + // re-start our crashing activity once it gets resumed again. + index--; + if (index >= 0) { + r = (HistoryRecord)mHistory.get(index); + if (r.state == ActivityState.RESUMED + || r.state == ActivityState.PAUSING + || r.state == ActivityState.PAUSED) { + if (!r.isHomeActivity) { + Slog.w(TAG, " Force finishing activity " + + r.intent.getComponent().flattenToShortString()); + finishActivityLocked(r, index, + Activity.RESULT_CANCELED, null, "crashed"); + } + } + } + } } // Bump up the crash count of any services currently running in the proc. @@ -8804,7 +8997,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } void startAppProblemLocked(ProcessRecord app) { - app.errorReportReceiver = getErrorReportReceiver(app); + app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver( + mContext, app.info.packageName, app.info.flags); skipCurrentReceiverLocked(app); } @@ -8822,7 +9016,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } r = mPendingBroadcast; if (r != null && r.curApp == app) { - if (DEBUG_BROADCAST) Log.v(TAG, + if (DEBUG_BROADCAST) Slog.v(TAG, "skip & discard pending app " + r); logBroadcastReceiverDiscard(r); finishReceiverLocked(r.receiver, r.resultCode, r.resultData, @@ -8834,42 +9028,241 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - public int handleApplicationError(IBinder app, int flags, - String tag, String shortMsg, String longMsg, byte[] crashData) { - AppErrorResult result = new AppErrorResult(); - ProcessRecord r = null; + /** + * Used by {@link com.android.internal.os.RuntimeInit} to report when an application crashes. + * The application process will exit immediately after this call returns. + * @param app object of the crashing app, null for the system server + * @param crashInfo describing the exception + */ + public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) { + ProcessRecord r = findAppProcess(app); + + EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(), + app == null ? "system" : (r == null ? "unknown" : r.processName), + r == null ? -1 : r.info.flags, + crashInfo.exceptionClassName, + crashInfo.exceptionMessage, + crashInfo.throwFileName, + crashInfo.throwLineNumber); + + addErrorToDropBox("crash", r, null, null, null, null, null, crashInfo); + + crashApplication(r, crashInfo); + } + + /** + * Used by {@link Log} via {@link com.android.internal.os.RuntimeInit} to report serious errors. + * @param app object of the crashing app, null for the system server + * @param tag reported by the caller + * @param crashInfo describing the context of the error + * @return true if the process should exit immediately (WTF is fatal) + */ + public boolean handleApplicationWtf(IBinder app, String tag, + ApplicationErrorReport.CrashInfo crashInfo) { + ProcessRecord r = findAppProcess(app); + + EventLog.writeEvent(EventLogTags.AM_WTF, Binder.getCallingPid(), + app == null ? "system" : (r == null ? "unknown" : r.processName), + r == null ? -1 : r.info.flags, + tag, crashInfo.exceptionMessage); + + addErrorToDropBox("wtf", r, null, null, tag, null, null, crashInfo); + + if (Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.WTF_IS_FATAL, 0) != 0) { + crashApplication(r, crashInfo); + return true; + } else { + return false; + } + } + + /** + * @param app object of some object (as stored in {@link com.android.internal.os.RuntimeInit}) + * @return the corresponding {@link ProcessRecord} object, or null if none could be found + */ + private ProcessRecord findAppProcess(IBinder app) { + if (app == null) { + return null; + } + synchronized (this) { - if (app != null) { - for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { - final int NA = apps.size(); - for (int ia=0; ia<NA; ia++) { - ProcessRecord p = apps.valueAt(ia); - if (p.thread != null && p.thread.asBinder() == app) { - r = p; - break; + for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { + final int NA = apps.size(); + for (int ia=0; ia<NA; ia++) { + ProcessRecord p = apps.valueAt(ia); + if (p.thread != null && p.thread.asBinder() == app) { + return p; + } + } + } + + Slog.w(TAG, "Can't find mystery application: " + app); + return null; + } + } + + /** + * Write a description of an error (crash, WTF, ANR) to the drop box. + * @param eventType to include in the drop box tag ("crash", "wtf", etc.) + * @param process which caused the error, null means the system server + * @param activity which triggered the error, null if unknown + * @param parent activity related to the error, null if unknown + * @param subject line related to the error, null if absent + * @param report in long form describing the error, null if absent + * @param logFile to include in the report, null if none + * @param crashInfo giving an application stack trace, null if absent + */ + public void addErrorToDropBox(String eventType, + ProcessRecord process, HistoryRecord activity, HistoryRecord parent, String subject, + final String report, final File logFile, + final ApplicationErrorReport.CrashInfo crashInfo) { + // NOTE -- this must never acquire the ActivityManagerService lock, + // otherwise the watchdog may be prevented from resetting the system. + + String prefix; + if (process == null || process.pid == MY_PID) { + prefix = "system_server_"; + } else if ((process.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + prefix = "system_app_"; + } else { + prefix = "data_app_"; + } + + final String dropboxTag = prefix + eventType; + final DropBoxManager dbox = (DropBoxManager) + mContext.getSystemService(Context.DROPBOX_SERVICE); + + // Exit early if the dropbox isn't configured to accept this report type. + if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return; + + final StringBuilder sb = new StringBuilder(1024); + if (process == null || process.pid == MY_PID) { + sb.append("Process: system_server\n"); + } else { + sb.append("Process: ").append(process.processName).append("\n"); + } + if (process != null) { + int flags = process.info.flags; + IPackageManager pm = ActivityThread.getPackageManager(); + sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n"); + for (String pkg : process.pkgList) { + sb.append("Package: ").append(pkg); + try { + PackageInfo pi = pm.getPackageInfo(pkg, 0); + if (pi != null) { + sb.append(" v").append(pi.versionCode); + if (pi.versionName != null) { + sb.append(" (").append(pi.versionName).append(")"); } } + } catch (RemoteException e) { + Slog.e(TAG, "Error getting package info: " + pkg, e); } + sb.append("\n"); } + } + if (activity != null) { + sb.append("Activity: ").append(activity.shortComponentName).append("\n"); + } + if (parent != null && parent.app != null && parent.app.pid != process.pid) { + sb.append("Parent-Process: ").append(parent.app.processName).append("\n"); + } + if (parent != null && parent != activity) { + sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n"); + } + if (subject != null) { + sb.append("Subject: ").append(subject).append("\n"); + } + sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); + sb.append("\n"); - if (r != null) { - // The application has crashed. Send the SIGQUIT to the process so - // that it can dump its state. - Process.sendSignal(r.pid, Process.SIGNAL_QUIT); - //Log.i(TAG, "Current system threads:"); - //Process.sendSignal(MY_PID, Process.SIGNAL_QUIT); + // Do the rest in a worker thread to avoid blocking the caller on I/O + // (After this point, we shouldn't access AMS internal data structures.) + Thread worker = new Thread("Error dump: " + dropboxTag) { + @Override + public void run() { + if (report != null) { + sb.append(report); + } + if (logFile != null) { + try { + sb.append(FileUtils.readTextFile(logFile, 128 * 1024, "\n\n[[TRUNCATED]]")); + } catch (IOException e) { + Slog.e(TAG, "Error reading " + logFile, e); + } + } + if (crashInfo != null && crashInfo.stackTrace != null) { + sb.append(crashInfo.stackTrace); + } + + String setting = Settings.Secure.ERROR_LOGCAT_PREFIX + dropboxTag; + int lines = Settings.Secure.getInt(mContext.getContentResolver(), setting, 0); + if (lines > 0) { + sb.append("\n"); + + // Merge several logcat streams, and take the last N lines + InputStreamReader input = null; + try { + java.lang.Process logcat = new ProcessBuilder("/system/bin/logcat", + "-v", "time", "-b", "events", "-b", "system", "-b", "main", + "-t", String.valueOf(lines)).redirectErrorStream(true).start(); + + try { logcat.getOutputStream().close(); } catch (IOException e) {} + try { logcat.getErrorStream().close(); } catch (IOException e) {} + input = new InputStreamReader(logcat.getInputStream()); + + int num; + char[] buf = new char[8192]; + while ((num = input.read(buf)) > 0) sb.append(buf, 0, num); + } catch (IOException e) { + Slog.e(TAG, "Error running logcat", e); + } finally { + if (input != null) try { input.close(); } catch (IOException e) {} + } + } + + dbox.addText(dropboxTag, sb.toString()); } + }; + + if (process == null || process.pid == MY_PID) { + worker.run(); // We may be about to die -- need to run this synchronously + } else { + worker.start(); + } + } + /** + * Bring up the "unexpected error" dialog box for a crashing app. + * Deal with edge cases (intercepts from instrumented applications, + * ActivityController, error intent receivers, that sort of thing). + * @param r the application crashing + * @param crashInfo describing the failure + */ + private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) { + long timeMillis = System.currentTimeMillis(); + String shortMsg = crashInfo.exceptionClassName; + String longMsg = crashInfo.exceptionMessage; + String stackTrace = crashInfo.stackTrace; + if (shortMsg != null && longMsg != null) { + longMsg = shortMsg + ": " + longMsg; + } else if (shortMsg != null) { + longMsg = shortMsg; + } + + AppErrorResult result = new AppErrorResult(); + synchronized (this) { if (mController != null) { try { String name = r != null ? r.processName : null; int pid = r != null ? r.pid : Binder.getCallingPid(); if (!mController.appCrashed(name, pid, - shortMsg, longMsg, crashData)) { - Log.w(TAG, "Force-killing crashed app " + name + shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) { + Slog.w(TAG, "Force-killing crashed app " + name + " at watcher's request"); Process.killProcess(pid); - return 0; + return; } } catch (RemoteException e) { mController = null; @@ -8880,29 +9273,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If this process is running instrumentation, finish it. if (r != null && r.instrumentationClass != null) { - Log.w(TAG, "Error in app " + r.processName + Slog.w(TAG, "Error in app " + r.processName + " running instrumentation " + r.instrumentationClass + ":"); - if (shortMsg != null) Log.w(TAG, " " + shortMsg); - if (longMsg != null) Log.w(TAG, " " + longMsg); + if (shortMsg != null) Slog.w(TAG, " " + shortMsg); + if (longMsg != null) Slog.w(TAG, " " + longMsg); Bundle info = new Bundle(); info.putString("shortMsg", shortMsg); info.putString("longMsg", longMsg); finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info); Binder.restoreCallingIdentity(origId); - return 0; + return; } - if (r != null) { - if (!makeAppCrashingLocked(r, tag, shortMsg, longMsg, crashData)) { - return 0; - } - } else { - Log.w(TAG, "Some application object " + app + " tag " + tag - + " has crashed, but I don't know who it is."); - Log.w(TAG, "ShortMsg:" + shortMsg); - Log.w(TAG, "LongMsg:" + longMsg); + // If we can't identify the process or it's already exceeded its crash quota, + // quit right away without showing a crash dialog. + if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) { Binder.restoreCallingIdentity(origId); - return 0; + return; } Message msg = Message.obtain(); @@ -8910,13 +9297,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HashMap data = new HashMap(); data.put("result", result); data.put("app", r); - data.put("flags", flags); - data.put("shortMsg", shortMsg); - data.put("longMsg", longMsg); - if (r != null && (r.info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { - // For system processes, submit crash data to the server. - data.put("crashData", crashData); - } msg.obj = data; mHandler.sendMessage(msg); @@ -8932,8 +9312,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen SystemClock.uptimeMillis()); } if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { - appErrorIntent = createAppErrorIntentLocked(r); - res = AppErrorDialog.FORCE_QUIT; + appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo); } } @@ -8941,15 +9320,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { mContext.startActivity(appErrorIntent); } catch (ActivityNotFoundException e) { - Log.w(TAG, "bug report receiver dissappeared", e); + Slog.w(TAG, "bug report receiver dissappeared", e); } } - - return res; } - - Intent createAppErrorIntentLocked(ProcessRecord r) { - ApplicationErrorReport report = createAppErrorReportLocked(r); + + Intent createAppErrorIntentLocked(ProcessRecord r, + long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) { + ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo); if (report == null) { return null; } @@ -8960,7 +9338,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return result; } - ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r) { + private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r, + long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) { if (r.errorReportReceiver == null) { return null; } @@ -8969,58 +9348,26 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return null; } - try { - ApplicationErrorReport report = new ApplicationErrorReport(); - report.packageName = r.info.packageName; - report.installerPackageName = r.errorReportReceiver.getPackageName(); - report.processName = r.processName; - - if (r.crashing) { - report.type = ApplicationErrorReport.TYPE_CRASH; - report.crashInfo = new ApplicationErrorReport.CrashInfo(); - - ByteArrayInputStream byteStream = new ByteArrayInputStream( - r.crashingReport.crashData); - DataInputStream dataStream = new DataInputStream(byteStream); - CrashData crashData = new CrashData(dataStream); - ThrowableData throwData = crashData.getThrowableData(); - - report.time = crashData.getTime(); - report.crashInfo.stackTrace = throwData.toString(); - - // Extract the source of the exception, useful for report - // clustering. Also extract the "deepest" non-null exception - // message. - String exceptionMessage = throwData.getMessage(); - while (throwData.getCause() != null) { - throwData = throwData.getCause(); - String msg = throwData.getMessage(); - if (msg != null && msg.length() > 0) { - exceptionMessage = msg; - } - } - StackTraceElementData trace = throwData.getStackTrace()[0]; - report.crashInfo.exceptionMessage = exceptionMessage; - report.crashInfo.exceptionClassName = throwData.getType(); - report.crashInfo.throwFileName = trace.getFileName(); - report.crashInfo.throwClassName = trace.getClassName(); - report.crashInfo.throwMethodName = trace.getMethodName(); - report.crashInfo.throwLineNumber = trace.getLineNumber(); - } else if (r.notResponding) { - report.type = ApplicationErrorReport.TYPE_ANR; - report.anrInfo = new ApplicationErrorReport.AnrInfo(); - - report.anrInfo.activity = r.notRespondingReport.tag; - report.anrInfo.cause = r.notRespondingReport.shortMsg; - report.anrInfo.info = r.notRespondingReport.longMsg; - } - - return report; - } catch (IOException e) { - // we don't send it + ApplicationErrorReport report = new ApplicationErrorReport(); + report.packageName = r.info.packageName; + report.installerPackageName = r.errorReportReceiver.getPackageName(); + report.processName = r.processName; + report.time = timeMillis; + report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + + if (r.crashing) { + report.type = ApplicationErrorReport.TYPE_CRASH; + report.crashInfo = crashInfo; + } else if (r.notResponding) { + report.type = ApplicationErrorReport.TYPE_ANR; + report.anrInfo = new ApplicationErrorReport.AnrInfo(); + + report.anrInfo.activity = r.notRespondingReport.tag; + report.anrInfo.cause = r.notRespondingReport.shortMsg; + report.anrInfo.info = r.notRespondingReport.longMsg; } - return null; + return report; } public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() { @@ -9030,9 +9377,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (this) { // iterate across all processes - final int N = mLRUProcesses.size(); - for (int i = 0; i < N; i++) { - ProcessRecord app = mLRUProcesses.get(i); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); if ((app.thread != null) && (app.crashing || app.notResponding)) { // This one's in trouble, so we'll generate a report for it // crashes are higher priority (in case there's a crash *and* an anr) @@ -9049,7 +9395,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } errList.add(report); } else { - Log.w(TAG, "Missing app error report, app = " + app.processName + + Slog.w(TAG, "Missing app error report, app = " + app.processName + " crashing = " + app.crashing + " notResponding = " + app.notResponding); } @@ -9065,9 +9411,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen List<ActivityManager.RunningAppProcessInfo> runList = null; synchronized (this) { // Iterate across all processes - final int N = mLRUProcesses.size(); - for (int i = 0; i < N; i++) { - ProcessRecord app = mLRUProcesses.get(i); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); if ((app.thread != null) && (!app.crashing && !app.notResponding)) { // Generate process state info for running application ActivityManager.RunningAppProcessInfo currApp = @@ -9075,7 +9420,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.pid, app.getPackageList()); currApp.uid = app.info.uid; int adj = app.curAdj; - if (adj >= CONTENT_PROVIDER_ADJ) { + if (adj >= EMPTY_APP_ADJ) { currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY; } else if (adj >= HIDDEN_APP_MIN_ADJ) { currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; @@ -9100,7 +9445,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (app.adjTarget instanceof ComponentName) { currApp.importanceReasonComponent = (ComponentName)app.adjTarget; } - //Log.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance + //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance // + " lru=" + currApp.lru); if (runList == null) { runList = new ArrayList<ActivityManager.RunningAppProcessInfo>(); @@ -9112,71 +9457,218 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return runList; } + public List<ApplicationInfo> getRunningExternalApplications() { + List<ActivityManager.RunningAppProcessInfo> runningApps = getRunningAppProcesses(); + List<ApplicationInfo> retList = new ArrayList<ApplicationInfo>(); + if (runningApps != null && runningApps.size() > 0) { + Set<String> extList = new HashSet<String>(); + for (ActivityManager.RunningAppProcessInfo app : runningApps) { + if (app.pkgList != null) { + for (String pkg : app.pkgList) { + extList.add(pkg); + } + } + } + IPackageManager pm = ActivityThread.getPackageManager(); + for (String pkg : extList) { + try { + ApplicationInfo info = pm.getApplicationInfo(pkg, 0); + if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { + retList.add(info); + } + } catch (RemoteException e) { + } + } + } + return retList; + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - synchronized (this) { - if (checkCallingPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump ActivityManager from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() - + " without permission " - + android.Manifest.permission.DUMP); + if (checkCallingPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump ActivityManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " without permission " + + android.Manifest.permission.DUMP); + return; + } + + boolean dumpAll = false; + + int opti = 0; + while (opti < args.length) { + String opt = args[opti]; + if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') { + break; + } + opti++; + if ("-a".equals(opt)) { + dumpAll = true; + } else if ("-h".equals(opt)) { + pw.println("Activity manager dump options:"); + pw.println(" [-a] [-h] [cmd] ..."); + pw.println(" cmd may be one of:"); + pw.println(" activities: activity stack state"); + pw.println(" broadcasts: broadcast state"); + pw.println(" intents: pending intent state"); + pw.println(" processes: process state"); + pw.println(" providers: content provider state"); + pw.println(" services: service state"); + pw.println(" service [name]: service client-side state"); return; + } else { + pw.println("Unknown argument: " + opt + "; use -h for help"); } - if (args.length != 0 && "service".equals(args[0])) { - dumpService(fd, pw, args); + } + + // Is the caller requesting to dump a particular piece of data? + if (opti < args.length) { + String cmd = args[opti]; + opti++; + if ("activities".equals(cmd) || "a".equals(cmd)) { + synchronized (this) { + dumpActivitiesLocked(fd, pw, args, opti, true, true); + } + return; + } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) { + synchronized (this) { + dumpBroadcastsLocked(fd, pw, args, opti, true); + } + return; + } else if ("intents".equals(cmd) || "i".equals(cmd)) { + synchronized (this) { + dumpPendingIntentsLocked(fd, pw, args, opti, true); + } + return; + } else if ("processes".equals(cmd) || "p".equals(cmd)) { + synchronized (this) { + dumpProcessesLocked(fd, pw, args, opti, true); + } + return; + } else if ("providers".equals(cmd) || "prov".equals(cmd)) { + synchronized (this) { + dumpProvidersLocked(fd, pw, args, opti, true); + } + return; + } else if ("service".equals(cmd)) { + dumpService(fd, pw, args, opti, true); + return; + } else if ("services".equals(cmd) || "s".equals(cmd)) { + synchronized (this) { + dumpServicesLocked(fd, pw, args, opti, true); + } return; } - pw.println("Activities in Current Activity Manager State:"); - dumpHistoryList(pw, mHistory, " ", "Hist", true); - pw.println(" "); - pw.println(" Running activities (most recent first):"); - dumpHistoryList(pw, mLRUActivities, " ", "Run", false); - if (mWaitingVisibleActivities.size() > 0) { + } + + // No piece of data specified, dump everything. + synchronized (this) { + boolean needSep; + if (dumpAll) { + pw.println("Providers in Current Activity Manager State:"); + } + needSep = dumpProvidersLocked(fd, pw, args, opti, dumpAll); + if (needSep) { pw.println(" "); - pw.println(" Activities waiting for another to become visible:"); - dumpHistoryList(pw, mWaitingVisibleActivities, " ", "Wait", false); } - if (mStoppingActivities.size() > 0) { + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + pw.println("Broadcasts in Current Activity Manager State:"); + } + needSep = dumpBroadcastsLocked(fd, pw, args, opti, dumpAll); + if (needSep) { pw.println(" "); - pw.println(" Activities waiting to stop:"); - dumpHistoryList(pw, mStoppingActivities, " ", "Stop", false); } - if (mFinishingActivities.size() > 0) { + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + pw.println("Services in Current Activity Manager State:"); + } + needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll); + if (needSep) { pw.println(" "); - pw.println(" Activities waiting to finish:"); - dumpHistoryList(pw, mFinishingActivities, " ", "Fin", false); } - - pw.println(" "); - pw.println(" mPausingActivity: " + mPausingActivity); - pw.println(" mResumedActivity: " + mResumedActivity); - pw.println(" mFocusedActivity: " + mFocusedActivity); - pw.println(" mLastPausedActivity: " + mLastPausedActivity); - - if (mRecentTasks.size() > 0) { + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + pw.println("PendingIntents in Current Activity Manager State:"); + } + needSep = dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll); + if (needSep) { pw.println(" "); - pw.println("Recent tasks in Current Activity Manager State:"); - - final int N = mRecentTasks.size(); - for (int i=0; i<N; i++) { - TaskRecord tr = mRecentTasks.get(i); - pw.print(" * Recent #"); pw.print(i); pw.print(": "); - pw.println(tr); - mRecentTasks.get(i).dump(pw, " "); - } } - + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + pw.println("Activities in Current Activity Manager State:"); + } + needSep = dumpActivitiesLocked(fd, pw, args, opti, dumpAll, !dumpAll); + if (needSep) { + pw.println(" "); + } + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + pw.println("Processes in Current Activity Manager State:"); + } + dumpProcessesLocked(fd, pw, args, opti, dumpAll); + } + } + + boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll, boolean needHeader) { + if (needHeader) { + pw.println(" Activity stack:"); + } + dumpHistoryList(pw, mHistory, " ", "Hist", true); + pw.println(" "); + pw.println(" Running activities (most recent first):"); + dumpHistoryList(pw, mLRUActivities, " ", "Run", false); + if (mWaitingVisibleActivities.size() > 0) { pw.println(" "); - pw.println(" mCurTask: " + mCurTask); - + pw.println(" Activities waiting for another to become visible:"); + dumpHistoryList(pw, mWaitingVisibleActivities, " ", "Wait", false); + } + if (mStoppingActivities.size() > 0) { + pw.println(" "); + pw.println(" Activities waiting to stop:"); + dumpHistoryList(pw, mStoppingActivities, " ", "Stop", false); + } + if (mFinishingActivities.size() > 0) { + pw.println(" "); + pw.println(" Activities waiting to finish:"); + dumpHistoryList(pw, mFinishingActivities, " ", "Fin", false); + } + + pw.println(" "); + pw.println(" mPausingActivity: " + mPausingActivity); + pw.println(" mResumedActivity: " + mResumedActivity); + pw.println(" mFocusedActivity: " + mFocusedActivity); + pw.println(" mLastPausedActivity: " + mLastPausedActivity); + + if (dumpAll && mRecentTasks.size() > 0) { pw.println(" "); - pw.println("Processes in Current Activity Manager State:"); + pw.println("Recent tasks in Current Activity Manager State:"); - boolean needSep = false; - int numPers = 0; + final int N = mRecentTasks.size(); + for (int i=0; i<N; i++) { + TaskRecord tr = mRecentTasks.get(i); + pw.print(" * Recent #"); pw.print(i); pw.print(": "); + pw.println(tr); + mRecentTasks.get(i).dump(pw, " "); + } + } + + pw.println(" "); + pw.println(" mCurTask: " + mCurTask); + + return true; + } + + boolean dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll) { + boolean needSep = false; + int numPers = 0; + if (dumpAll) { for (SparseArray<ProcessRecord> procs : mProcessNames.getMap().values()) { final int NA = procs.size(); for (int ia=0; ia<NA; ia++) { @@ -9194,142 +9686,152 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } } - - if (mLRUProcesses.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Running processes (most recent first):"); - dumpProcessList(pw, mLRUProcesses, " ", - "App ", "PERS", true); - needSep = true; - } + } + + if (mLruProcesses.size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Running processes (most recent first):"); + dumpProcessList(pw, this, mLruProcesses, " ", + "App ", "PERS", true); + needSep = true; + } - synchronized (mPidsSelfLocked) { - if (mPidsSelfLocked.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" PID mappings:"); - for (int i=0; i<mPidsSelfLocked.size(); i++) { - pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i)); - pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i)); - } - } - } - - if (mForegroundProcesses.size() > 0) { + synchronized (mPidsSelfLocked) { + if (mPidsSelfLocked.size() > 0) { if (needSep) pw.println(" "); needSep = true; - pw.println(" Foreground Processes:"); - for (int i=0; i<mForegroundProcesses.size(); i++) { - pw.print(" PID #"); pw.print(mForegroundProcesses.keyAt(i)); - pw.print(": "); pw.println(mForegroundProcesses.valueAt(i)); + pw.println(" PID mappings:"); + for (int i=0; i<mPidsSelfLocked.size(); i++) { + pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i)); + pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i)); } } - - if (mPersistentStartingProcesses.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Persisent processes that are starting:"); - dumpProcessList(pw, mPersistentStartingProcesses, " ", - "Starting Norm", "Restarting PERS", false); + } + + if (mForegroundProcesses.size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Foreground Processes:"); + for (int i=0; i<mForegroundProcesses.size(); i++) { + pw.print(" PID #"); pw.print(mForegroundProcesses.keyAt(i)); + pw.print(": "); pw.println(mForegroundProcesses.valueAt(i)); } + } + + if (mPersistentStartingProcesses.size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Persisent processes that are starting:"); + dumpProcessList(pw, this, mPersistentStartingProcesses, " ", + "Starting Norm", "Restarting PERS", false); + } - if (mStartingProcesses.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Processes that are starting:"); - dumpProcessList(pw, mStartingProcesses, " ", - "Starting Norm", "Starting PERS", false); - } + if (mStartingProcesses.size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Processes that are starting:"); + dumpProcessList(pw, this, mStartingProcesses, " ", + "Starting Norm", "Starting PERS", false); + } - if (mRemovedProcesses.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Processes that are being removed:"); - dumpProcessList(pw, mRemovedProcesses, " ", - "Removed Norm", "Removed PERS", false); - } - - if (mProcessesOnHold.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Processes that are on old until the system is ready:"); - dumpProcessList(pw, mProcessesOnHold, " ", - "OnHold Norm", "OnHold PERS", false); - } + if (mRemovedProcesses.size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Processes that are being removed:"); + dumpProcessList(pw, this, mRemovedProcesses, " ", + "Removed Norm", "Removed PERS", false); + } + + if (mProcessesOnHold.size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Processes that are on old until the system is ready:"); + dumpProcessList(pw, this, mProcessesOnHold, " ", + "OnHold Norm", "OnHold PERS", false); + } - if (mProcessesToGc.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Processes that are waiting to GC:"); - long now = SystemClock.uptimeMillis(); - for (int i=0; i<mProcessesToGc.size(); i++) { - ProcessRecord proc = mProcessesToGc.get(i); - pw.print(" Process "); pw.println(proc); - pw.print(" lowMem="); pw.print(proc.reportLowMemory); - pw.print(", last gced="); - pw.print(now-proc.lastRequestedGc); - pw.print(" ms ago, last lowMem="); - pw.print(now-proc.lastLowMemory); - pw.println(" ms ago"); - - } + if (mProcessesToGc.size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Processes that are waiting to GC:"); + long now = SystemClock.uptimeMillis(); + for (int i=0; i<mProcessesToGc.size(); i++) { + ProcessRecord proc = mProcessesToGc.get(i); + pw.print(" Process "); pw.println(proc); + pw.print(" lowMem="); pw.print(proc.reportLowMemory); + pw.print(", last gced="); + pw.print(now-proc.lastRequestedGc); + pw.print(" ms ago, last lowMem="); + pw.print(now-proc.lastLowMemory); + pw.println(" ms ago"); + } - - if (mProcessCrashTimes.getMap().size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Time since processes crashed:"); - long now = SystemClock.uptimeMillis(); - for (Map.Entry<String, SparseArray<Long>> procs - : mProcessCrashTimes.getMap().entrySet()) { - SparseArray<Long> uids = procs.getValue(); - final int N = uids.size(); - for (int i=0; i<N; i++) { - pw.print(" Process "); pw.print(procs.getKey()); - pw.print(" uid "); pw.print(uids.keyAt(i)); - pw.print(": last crashed "); - pw.print((now-uids.valueAt(i))); - pw.println(" ms ago"); - } + } + + if (mProcessCrashTimes.getMap().size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Time since processes crashed:"); + long now = SystemClock.uptimeMillis(); + for (Map.Entry<String, SparseArray<Long>> procs + : mProcessCrashTimes.getMap().entrySet()) { + SparseArray<Long> uids = procs.getValue(); + final int N = uids.size(); + for (int i=0; i<N; i++) { + pw.print(" Process "); pw.print(procs.getKey()); + pw.print(" uid "); pw.print(uids.keyAt(i)); + pw.print(": last crashed "); + pw.print((now-uids.valueAt(i))); + pw.println(" ms ago"); } } + } - if (mBadProcesses.getMap().size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Bad processes:"); - for (Map.Entry<String, SparseArray<Long>> procs - : mBadProcesses.getMap().entrySet()) { - SparseArray<Long> uids = procs.getValue(); - final int N = uids.size(); - for (int i=0; i<N; i++) { - pw.print(" Bad process "); pw.print(procs.getKey()); - pw.print(" uid "); pw.print(uids.keyAt(i)); - pw.print(": crashed at time "); - pw.println(uids.valueAt(i)); - } + if (mBadProcesses.getMap().size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Bad processes:"); + for (Map.Entry<String, SparseArray<Long>> procs + : mBadProcesses.getMap().entrySet()) { + SparseArray<Long> uids = procs.getValue(); + final int N = uids.size(); + for (int i=0; i<N; i++) { + pw.print(" Bad process "); pw.print(procs.getKey()); + pw.print(" uid "); pw.print(uids.keyAt(i)); + pw.print(": crashed at time "); + pw.println(uids.valueAt(i)); } } + } - pw.println(" "); + pw.println(" "); + pw.println(" mHomeProcess: " + mHomeProcess); + pw.println(" mConfiguration: " + mConfiguration); + pw.println(" mConfigWillChange: " + mConfigWillChange); + pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown); + if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient + || mOrigWaitForDebugger) { + pw.println(" mDebugApp=" + mDebugApp + "/orig=" + mOrigDebugApp + + " mDebugTransient=" + mDebugTransient + + " mOrigWaitForDebugger=" + mOrigWaitForDebugger); + } + if (mAlwaysFinishActivities || mController != null) { + pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities + + " mController=" + mController); + } + if (dumpAll) { pw.println(" Total persistent processes: " + numPers); - pw.println(" mHomeProcess: " + mHomeProcess); - pw.println(" mConfiguration: " + mConfiguration); pw.println(" mStartRunning=" + mStartRunning + " mSystemReady=" + mSystemReady + " mBooting=" + mBooting + " mBooted=" + mBooted + " mFactoryTest=" + mFactoryTest); - pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown); pw.println(" mGoingToSleep=" + mGoingToSleep); pw.println(" mLaunchingActivity=" + mLaunchingActivity); - pw.println(" mDebugApp=" + mDebugApp + "/orig=" + mOrigDebugApp - + " mDebugTransient=" + mDebugTransient - + " mOrigWaitForDebugger=" + mOrigWaitForDebugger); - pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities - + " mController=" + mController); } + + return true; } /** @@ -9340,20 +9842,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * - the first arg isn't the flattened component name of an existing service: * dump all services whose component contains the first arg as a substring */ - protected void dumpService(FileDescriptor fd, PrintWriter pw, String[] args) { + protected void dumpService(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll) { String[] newArgs; String componentNameString; ServiceRecord r; - if (args.length == 1) { + if (opti >= args.length) { componentNameString = null; newArgs = EMPTY_STRING_ARRAY; r = null; } else { - componentNameString = args[1]; + componentNameString = args[opti]; + opti++; ComponentName componentName = ComponentName.unflattenFromString(componentNameString); r = componentName != null ? mServices.get(componentName) : null; - newArgs = new String[args.length - 2]; - if (args.length > 2) System.arraycopy(args, 2, newArgs, 0, args.length - 2); + newArgs = new String[args.length - opti]; + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); } if (r != null) { @@ -9387,19 +9891,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - void dumpBroadcasts(PrintWriter pw) { - synchronized (this) { - if (checkCallingPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump ActivityManager from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() - + " without permission " - + android.Manifest.permission.DUMP); - return; - } - pw.println("Broadcasts in Current Activity Manager State:"); - + boolean dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll) { + boolean needSep = false; + + if (dumpAll) { if (mRegisteredReceivers.size() > 0) { pw.println(" "); pw.println(" Registered Receivers:"); @@ -9410,38 +9906,42 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.dump(pw, " "); } } - + pw.println(" "); pw.println("Receiver Resolver Table:"); - mReceiverResolver.dump(pw, " "); - - if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0 - || mPendingBroadcast != null) { - if (mParallelBroadcasts.size() > 0) { - pw.println(" "); - pw.println(" Active broadcasts:"); - } - for (int i=mParallelBroadcasts.size()-1; i>=0; i--) { - pw.println(" Broadcast #" + i + ":"); - mParallelBroadcasts.get(i).dump(pw, " "); - } - if (mOrderedBroadcasts.size() > 0) { - pw.println(" "); - pw.println(" Active serialized broadcasts:"); - } - for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) { - pw.println(" Serialized Broadcast #" + i + ":"); - mOrderedBroadcasts.get(i).dump(pw, " "); - } + mReceiverResolver.dump(pw, null, " ", null); + needSep = true; + } + + if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0 + || mPendingBroadcast != null) { + if (mParallelBroadcasts.size() > 0) { pw.println(" "); - pw.println(" Pending broadcast:"); - if (mPendingBroadcast != null) { - mPendingBroadcast.dump(pw, " "); - } else { - pw.println(" (null)"); - } + pw.println(" Active broadcasts:"); + } + for (int i=mParallelBroadcasts.size()-1; i>=0; i--) { + pw.println(" Broadcast #" + i + ":"); + mParallelBroadcasts.get(i).dump(pw, " "); + } + if (mOrderedBroadcasts.size() > 0) { + pw.println(" "); + pw.println(" Active ordered broadcasts:"); + } + for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) { + pw.println(" Serialized Broadcast #" + i + ":"); + mOrderedBroadcasts.get(i).dump(pw, " "); } + pw.println(" "); + pw.println(" Pending broadcast:"); + if (mPendingBroadcast != null) { + mPendingBroadcast.dump(pw, " "); + } else { + pw.println(" (null)"); + } + needSep = true; + } + if (dumpAll) { pw.println(" "); pw.println(" Historical broadcasts:"); for (int i=0; i<MAX_BROADCAST_HISTORY; i++) { @@ -9452,54 +9952,50 @@ public final class ActivityManagerService extends ActivityManagerNative implemen pw.println(" Historical Broadcast #" + i + ":"); r.dump(pw, " "); } - + needSep = true; + } + + if (mStickyBroadcasts != null) { pw.println(" "); - pw.println(" mBroadcastsScheduled=" + mBroadcastsScheduled); - if (mStickyBroadcasts != null) { - pw.println(" "); - pw.println(" Sticky broadcasts:"); - StringBuilder sb = new StringBuilder(128); - for (Map.Entry<String, ArrayList<Intent>> ent - : mStickyBroadcasts.entrySet()) { - pw.print(" * Sticky action "); pw.print(ent.getKey()); - pw.println(":"); - ArrayList<Intent> intents = ent.getValue(); - final int N = intents.size(); - for (int i=0; i<N; i++) { - sb.setLength(0); - sb.append(" Intent: "); - intents.get(i).toShortString(sb, true, false); - pw.println(sb.toString()); - Bundle bundle = intents.get(i).getExtras(); - if (bundle != null) { - pw.print(" "); - pw.println(bundle.toString()); - } + pw.println(" Sticky broadcasts:"); + StringBuilder sb = new StringBuilder(128); + for (Map.Entry<String, ArrayList<Intent>> ent + : mStickyBroadcasts.entrySet()) { + pw.print(" * Sticky action "); pw.print(ent.getKey()); + pw.println(":"); + ArrayList<Intent> intents = ent.getValue(); + final int N = intents.size(); + for (int i=0; i<N; i++) { + sb.setLength(0); + sb.append(" Intent: "); + intents.get(i).toShortString(sb, true, false); + pw.println(sb.toString()); + Bundle bundle = intents.get(i).getExtras(); + if (bundle != null) { + pw.print(" "); + pw.println(bundle.toString()); } } } - + needSep = true; + } + + if (dumpAll) { pw.println(" "); + pw.println(" mBroadcastsScheduled=" + mBroadcastsScheduled); pw.println(" mHandler:"); mHandler.dump(new PrintWriterPrinter(pw), " "); + needSep = true; } + + return needSep; } - void dumpServices(PrintWriter pw) { - synchronized (this) { - if (checkCallingPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump ActivityManager from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() - + " without permission " - + android.Manifest.permission.DUMP); - return; - } - pw.println("Services in Current Activity Manager State:"); - - boolean needSep = false; + boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll) { + boolean needSep = false; + if (dumpAll) { if (mServices.size() > 0) { pw.println(" Active services:"); Iterator<ServiceRecord> it = mServices.values().iterator(); @@ -9510,40 +10006,42 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } needSep = true; } + } - if (mPendingServices.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Pending services:"); - for (int i=0; i<mPendingServices.size(); i++) { - ServiceRecord r = mPendingServices.get(i); - pw.print(" * Pending "); pw.println(r); - r.dump(pw, " "); - } - needSep = true; + if (mPendingServices.size() > 0) { + if (needSep) pw.println(" "); + pw.println(" Pending services:"); + for (int i=0; i<mPendingServices.size(); i++) { + ServiceRecord r = mPendingServices.get(i); + pw.print(" * Pending "); pw.println(r); + r.dump(pw, " "); } + needSep = true; + } - if (mRestartingServices.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Restarting services:"); - for (int i=0; i<mRestartingServices.size(); i++) { - ServiceRecord r = mRestartingServices.get(i); - pw.print(" * Restarting "); pw.println(r); - r.dump(pw, " "); - } - needSep = true; + if (mRestartingServices.size() > 0) { + if (needSep) pw.println(" "); + pw.println(" Restarting services:"); + for (int i=0; i<mRestartingServices.size(); i++) { + ServiceRecord r = mRestartingServices.get(i); + pw.print(" * Restarting "); pw.println(r); + r.dump(pw, " "); } + needSep = true; + } - if (mStoppingServices.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Stopping services:"); - for (int i=0; i<mStoppingServices.size(); i++) { - ServiceRecord r = mStoppingServices.get(i); - pw.print(" * Stopping "); pw.println(r); - r.dump(pw, " "); - } - needSep = true; + if (mStoppingServices.size() > 0) { + if (needSep) pw.println(" "); + pw.println(" Stopping services:"); + for (int i=0; i<mStoppingServices.size(); i++) { + ServiceRecord r = mStoppingServices.get(i); + pw.print(" * Stopping "); pw.println(r); + r.dump(pw, " "); } + needSep = true; + } + if (dumpAll) { if (mServiceConnections.size() > 0) { if (needSep) pw.println(" "); pw.println(" Connection bindings to services:"); @@ -9554,26 +10052,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen pw.print(" * "); pw.println(r); r.dump(pw, " "); } + needSep = true; } } + + return needSep; } - void dumpProviders(PrintWriter pw) { - synchronized (this) { - if (checkCallingPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump ActivityManager from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() - + " without permission " - + android.Manifest.permission.DUMP); - return; - } - - pw.println("Content Providers in Current Activity Manager State:"); - - boolean needSep = false; + boolean dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll) { + boolean needSep = false; + if (dumpAll) { if (mProvidersByClass.size() > 0) { if (needSep) pw.println(" "); pw.println(" Published content providers (by class):"); @@ -9586,7 +10076,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } needSep = true; } - + if (mProvidersByName.size() > 0) { pw.println(" "); pw.println(" Authority to provider mappings:"); @@ -9599,55 +10089,50 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } needSep = true; } + } - if (mLaunchingProviders.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Launching content providers:"); - for (int i=mLaunchingProviders.size()-1; i>=0; i--) { - pw.print(" Launching #"); pw.print(i); pw.print(": "); - pw.println(mLaunchingProviders.get(i)); - } - needSep = true; + if (mLaunchingProviders.size() > 0) { + if (needSep) pw.println(" "); + pw.println(" Launching content providers:"); + for (int i=mLaunchingProviders.size()-1; i>=0; i--) { + pw.print(" Launching #"); pw.print(i); pw.print(": "); + pw.println(mLaunchingProviders.get(i)); } + needSep = true; + } - if (mGrantedUriPermissions.size() > 0) { - pw.println(); - pw.println("Granted Uri Permissions:"); - for (int i=0; i<mGrantedUriPermissions.size(); i++) { - int uid = mGrantedUriPermissions.keyAt(i); - HashMap<Uri, UriPermission> perms - = mGrantedUriPermissions.valueAt(i); - pw.print(" * UID "); pw.print(uid); - pw.println(" holds:"); - for (UriPermission perm : perms.values()) { - pw.print(" "); pw.println(perm); - perm.dump(pw, " "); - } + if (mGrantedUriPermissions.size() > 0) { + pw.println(); + pw.println("Granted Uri Permissions:"); + for (int i=0; i<mGrantedUriPermissions.size(); i++) { + int uid = mGrantedUriPermissions.keyAt(i); + HashMap<Uri, UriPermission> perms + = mGrantedUriPermissions.valueAt(i); + pw.print(" * UID "); pw.print(uid); + pw.println(" holds:"); + for (UriPermission perm : perms.values()) { + pw.print(" "); pw.println(perm); + perm.dump(pw, " "); } } + needSep = true; } + + return needSep; } - void dumpSenders(PrintWriter pw) { - synchronized (this) { - if (checkCallingPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump ActivityManager from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() - + " without permission " - + android.Manifest.permission.DUMP); - return; - } - - pw.println("Pending Intents in Current Activity Manager State:"); - + boolean dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll) { + boolean needSep = false; + + if (dumpAll) { if (this.mIntentSenderRecords.size() > 0) { Iterator<WeakReference<PendingIntentRecord>> it = mIntentSenderRecords.values().iterator(); while (it.hasNext()) { WeakReference<PendingIntentRecord> ref = it.next(); PendingIntentRecord rec = ref != null ? ref.get(): null; + needSep = true; if (rec != null) { pw.print(" * "); pw.println(rec); rec.dump(pw, " "); @@ -9657,6 +10142,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } } + + return needSep; } private static final void dumpHistoryList(PrintWriter pw, List list, @@ -9683,7 +10170,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private static final int dumpProcessList(PrintWriter pw, List list, + private static String buildOomTag(String prefix, String space, int val, int base) { + if (val == base) { + if (space == null) return prefix; + return prefix + " "; + } + return prefix + "+" + Integer.toString(val-base); + } + + private static final int dumpProcessList(PrintWriter pw, + ActivityManagerService service, List list, String prefix, String normalLabel, String persistentLabel, boolean inclOomAdj) { int numPers = 0; @@ -9694,12 +10190,46 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " #" + i + ":"); r.dump(pw, prefix + " "); } else if (inclOomAdj) { - pw.println(String.format("%s%s #%2d: adj=%4d/%d %s (%s)", + String oomAdj; + if (r.setAdj >= EMPTY_APP_ADJ) { + oomAdj = buildOomTag("empty", null, r.setAdj, EMPTY_APP_ADJ); + } else if (r.setAdj >= HIDDEN_APP_MIN_ADJ) { + oomAdj = buildOomTag("bak", " ", r.setAdj, HIDDEN_APP_MIN_ADJ); + } else if (r.setAdj >= HOME_APP_ADJ) { + oomAdj = buildOomTag("home ", null, r.setAdj, HOME_APP_ADJ); + } else if (r.setAdj >= SECONDARY_SERVER_ADJ) { + oomAdj = buildOomTag("svc", " ", r.setAdj, SECONDARY_SERVER_ADJ); + } else if (r.setAdj >= BACKUP_APP_ADJ) { + oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ); + } else if (r.setAdj >= VISIBLE_APP_ADJ) { + oomAdj = buildOomTag("vis ", null, r.setAdj, VISIBLE_APP_ADJ); + } else if (r.setAdj >= FOREGROUND_APP_ADJ) { + oomAdj = buildOomTag("fore ", null, r.setAdj, FOREGROUND_APP_ADJ); + } else if (r.setAdj >= CORE_SERVER_ADJ) { + oomAdj = buildOomTag("core ", null, r.setAdj, CORE_SERVER_ADJ); + } else if (r.setAdj >= SYSTEM_ADJ) { + oomAdj = buildOomTag("sys ", null, r.setAdj, SYSTEM_ADJ); + } else { + oomAdj = Integer.toString(r.setAdj); + } + String schedGroup; + switch (r.setSchedGroup) { + case Process.THREAD_GROUP_BG_NONINTERACTIVE: + schedGroup = "B"; + break; + case Process.THREAD_GROUP_DEFAULT: + schedGroup = "F"; + break; + default: + schedGroup = Integer.toString(r.setSchedGroup); + break; + } + pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)", prefix, (r.persistent ? persistentLabel : normalLabel), - i, r.setAdj, r.setSchedGroup, r.toString(), r.adjType)); + i, oomAdj, schedGroup, r.toShortString(), r.adjType)); if (r.adjSource != null || r.adjTarget != null) { pw.println(prefix + " " + r.adjTarget - + " used by " + r.adjSource); + + "<=" + r.adjSource); } } else { pw.println(String.format("%s%s #%2d: %s", @@ -9713,7 +10243,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return numPers; } - private static final void dumpApplicationMemoryUsage(FileDescriptor fd, + static final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, List list, String prefix, String[] args) { final boolean isCheckinRequest = scanArgs(args, "--checkin"); long uptime = SystemClock.uptimeMillis(); @@ -9767,12 +10297,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int count = mHistory.size(); // convert the token to an entry in the history. - HistoryRecord r = null; int index = -1; for (int i=count-1; i>=0; i--) { Object o = mHistory.get(i); if (o == token) { - r = (HistoryRecord)o; index = i; break; } @@ -9801,7 +10329,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen //c.conn.connected(r.className, null); } catch (Exception e) { // todo: this should be asynchronous! - Log.w(TAG, "Exception thrown disconnected servce " + Slog.w(TAG, "Exception thrown disconnected servce " + r.shortName + " from app " + app.processName, e); } @@ -9841,7 +10369,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = sr.bindings.values().iterator(); while (bindings.hasNext()) { IntentBindRecord b = bindings.next(); - if (DEBUG_SERVICE) Log.v(TAG, "Killing binding " + b + if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b + ": shouldUnbind=" + b.hasBound); b.binder = null; b.requested = b.received = b.hasBound = false; @@ -9849,9 +10377,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (sr.crashCount >= 2) { - Log.w(TAG, "Service crashed " + sr.crashCount + Slog.w(TAG, "Service crashed " + sr.crashCount + " times, stopping: " + sr); - EventLog.writeEvent(LOG_AM_SERVICE_CRASHED_TOO_MUCH, + EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH, sr.crashCount, sr.shortName, app.pid); bringDownServiceLocked(sr, true); } else if (!allowRestart) { @@ -9911,10 +10439,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (!capp.persistent && capp.thread != null && capp.pid != 0 && capp.pid != MY_PID) { - Log.i(TAG, "Killing app " + capp.processName - + " (pid " + capp.pid - + ") because provider " + cpr.info.name - + " is in dying process " + proc.processName); + Slog.i(TAG, "Kill " + capp.processName + + " (pid " + capp.pid + "): provider " + cpr.info.name + + " in dying process " + proc.processName); + EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid, + capp.processName, capp.setAdj, "dying provider " + proc.processName); Process.killProcess(capp.pid); } } @@ -9930,7 +10459,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final void cleanUpApplicationRecordLocked(ProcessRecord app, boolean restarting, int index) { if (index >= 0) { - mLRUProcesses.remove(index); + mLruProcesses.remove(index); } mProcessesToGc.remove(app); @@ -10039,7 +10568,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If the app is undergoing backup, tell the backup manager about it if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) { - if (DEBUG_BACKUP) Log.d(TAG, "App " + mBackupTarget.appInfo + " died during backup"); + if (DEBUG_BACKUP) Slog.d(TAG, "App " + mBackupTarget.appInfo + " died during backup"); try { IBackupManager bm = IBackupManager.Stub.asInterface( ServiceManager.getService(Context.BACKUP_SERVICE)); @@ -10056,7 +10585,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (!app.persistent) { - if (DEBUG_PROCESSES) Log.v(TAG, + if (DEBUG_PROCESSES) Slog.v(TAG, "Removing non-persistent process during cleanup: " + app); mProcessNames.remove(app.processName, app.info.uid); } else if (!app.removed) { @@ -10139,7 +10668,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.startRequested) { info.flags |= ActivityManager.RunningServiceInfo.FLAG_STARTED; } - if (r.app != null && r.app.pid == Process.myPid()) { + if (r.app != null && r.app.pid == MY_PID) { info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS; } if (r.app != null && r.app.persistent) { @@ -10245,7 +10774,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (checkComponentPermission(r.permission, callingPid, callingUid, r.exported ? -1 : r.appInfo.uid) != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Permission Denial: Accessing service " + r.name + Slog.w(TAG, "Permission Denial: Accessing service " + r.name + " from pid=" + callingPid + ", uid=" + callingUid + " requires " + r.permission); @@ -10286,7 +10815,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null; if (sInfo == null) { - Log.w(TAG, "Unable to start service " + service + + Slog.w(TAG, "Unable to start service " + service + ": not found"); return null; } @@ -10328,7 +10857,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (checkComponentPermission(r.permission, callingPid, callingUid, r.exported ? -1 : r.appInfo.uid) != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Permission Denial: Accessing service " + r.name + Slog.w(TAG, "Permission Denial: Accessing service " + r.name + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + r.permission); @@ -10364,7 +10893,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen while (i < N) { try { ServiceRecord.StartItem si = r.pendingStarts.get(i); - if (DEBUG_SERVICE) Log.v(TAG, "Sending arguments to service: " + if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to service: " + r.name + " " + r.intent + " args=" + si.intent); if (si.intent == null && N > 1) { // If somehow we got a dummy start at the front, then @@ -10394,7 +10923,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // care of this. break; } catch (Exception e) { - Log.w(TAG, "Unexpected exception", e); + Slog.w(TAG, "Unexpected exception", e); break; } } @@ -10417,7 +10946,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if ((!i.requested || rebind) && i.apps.size() > 0) { try { bumpServiceExecutingLocked(r); - if (DEBUG_SERVICE) Log.v(TAG, "Connecting binding " + i + if (DEBUG_SERVICE) Slog.v(TAG, "Connecting binding " + i + ": shouldUnbind=" + i.hasBound); r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind); if (!rebind) { @@ -10453,15 +10982,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.services.add(r); bumpServiceExecutingLocked(r); - updateLRUListLocked(app, true); + updateLruProcessLocked(app, true, true); boolean created = false; try { - if (DEBUG_SERVICE) Log.v(TAG, "Scheduling start service: " + if (DEBUG_SERVICE) Slog.v(TAG, "Scheduling start service: " + r.name + " " + r.intent); mStringBuilder.setLength(0); r.intent.getIntent().toShortString(mStringBuilder, false, true); - EventLog.writeEvent(LOG_AM_CREATE_SERVICE, + EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE, System.identityHashCode(r), r.shortName, mStringBuilder.toString(), r.app.pid); synchronized (r.stats.getBatteryStats()) { @@ -10518,7 +11047,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (minDuration < dur) minDuration = dur; if (resetTime < dur) resetTime = dur; } else { - Log.w(TAG, "Canceling start item " + si.intent + " in service " + Slog.w(TAG, "Canceling start item " + si.intent + " in service " + r.name); canceled = true; } @@ -10577,16 +11106,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mHandler.removeCallbacks(r.restarter); mHandler.postAtTime(r.restarter, r.nextRestartTime); r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay; - Log.w(TAG, "Scheduling restart of crashed service " + Slog.w(TAG, "Scheduling restart of crashed service " + r.shortName + " in " + r.restartDelay + "ms"); - EventLog.writeEvent(LOG_AM_SCHEDULE_SERVICE_RESTART, + EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART, r.shortName, r.restartDelay); - Message msg = Message.obtain(); - msg.what = SERVICE_ERROR_MSG; - msg.obj = r; - mHandler.sendMessage(msg); - return canceled; } @@ -10609,7 +11133,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final boolean bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean whileRestarting) { - //Log.i(TAG, "Bring up service:"); + //Slog.i(TAG, "Bring up service:"); //r.dump(" "); if (r.app != null && r.app.thread != null) { @@ -10622,7 +11146,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return true; } - if (DEBUG_SERVICE) Log.v(TAG, "Bringing up service " + r.name + if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up service " + r.name + " " + r.intent); // We are now bringing the service up, so no longer in the @@ -10636,7 +11160,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen realStartServiceLocked(r, app); return true; } catch (RemoteException e) { - Log.w(TAG, "Exception when starting service " + r.shortName, e); + Slog.w(TAG, "Exception when starting service " + r.shortName, e); } // If a dead object exception was thrown -- fall through to @@ -10647,7 +11171,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // to be executed when the app comes up. if (startProcessLocked(appName, r.appInfo, true, intentFlags, "service", r.name, false) == null) { - Log.w(TAG, "Unable to launch app " + Slog.w(TAG, "Unable to launch app " + r.appInfo.packageName + "/" + r.appInfo.uid + " for service " + r.intent.getIntent() + ": process is bad"); @@ -10663,7 +11187,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private final void bringDownServiceLocked(ServiceRecord r, boolean force) { - //Log.i(TAG, "Bring down service:"); + //Slog.i(TAG, "Bring down service:"); //r.dump(" "); // Does it still need to run? @@ -10692,7 +11216,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // todo: shouldn't be a synchronous call! c.conn.connected(r.name, null); } catch (Exception e) { - Log.w(TAG, "Failure disconnecting service " + r.name + + Slog.w(TAG, "Failure disconnecting service " + r.name + " to connection " + c.conn.asBinder() + " (in " + c.binding.client.processName + ")", e); } @@ -10704,7 +11228,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Iterator<IntentBindRecord> it = r.bindings.values().iterator(); while (it.hasNext()) { IntentBindRecord ibr = it.next(); - if (DEBUG_SERVICE) Log.v(TAG, "Bringing down binding " + ibr + if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down binding " + ibr + ": hasBound=" + ibr.hasBound); if (r.app != null && r.app.thread != null && ibr.hasBound) { try { @@ -10714,7 +11238,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app.thread.scheduleUnbindService(r, ibr.intent.getIntent()); } catch (Exception e) { - Log.w(TAG, "Exception when unbinding service " + Slog.w(TAG, "Exception when unbinding service " + r.shortName, e); serviceDoneExecutingLocked(r, true); } @@ -10722,15 +11246,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - if (DEBUG_SERVICE) Log.v(TAG, "Bringing down service " + r.name + if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down service " + r.name + " " + r.intent); - EventLog.writeEvent(LOG_AM_DESTROY_SERVICE, + EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE, System.identityHashCode(r), r.shortName, (r.app != null) ? r.app.pid : -1); mServices.remove(r.name); mServicesByIntent.remove(r.intent); - if (localLOGV) Log.v(TAG, "BRING DOWN SERVICE: " + r.shortName); + if (localLOGV) Slog.v(TAG, "BRING DOWN SERVICE: " + r.shortName); r.totalRestartCount = 0; unscheduleServiceRestartLocked(r); @@ -10739,7 +11263,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen for (int i=0; i<N; i++) { if (mPendingServices.get(i) == r) { mPendingServices.remove(i); - if (DEBUG_SERVICE) Log.v( + if (DEBUG_SERVICE) Slog.v( TAG, "Removed pending service: " + r.shortName); i--; N--; @@ -10762,24 +11286,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app.services.remove(r); if (r.app.thread != null) { try { - if (DEBUG_SERVICE) Log.v(TAG, + if (DEBUG_SERVICE) Slog.v(TAG, "Stopping service: " + r.shortName); bumpServiceExecutingLocked(r); mStoppingServices.add(r); updateOomAdjLocked(r.app); r.app.thread.scheduleStopService(r); } catch (Exception e) { - Log.w(TAG, "Exception when stopping service " + Slog.w(TAG, "Exception when stopping service " + r.shortName, e); serviceDoneExecutingLocked(r, true); } updateServiceForegroundLocked(r.app, false); } else { - if (DEBUG_SERVICE) Log.v( + if (DEBUG_SERVICE) Slog.v( TAG, "Removed service that has no process: " + r.shortName); } } else { - if (DEBUG_SERVICE) Log.v( + if (DEBUG_SERVICE) Slog.v( TAG, "Removed service that is not running: " + r.shortName); } } @@ -10788,7 +11312,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Intent service, String resolvedType, int callingPid, int callingUid) { synchronized(this) { - if (DEBUG_SERVICE) Log.v(TAG, "startService: " + service + if (DEBUG_SERVICE) Slog.v(TAG, "startService: " + service + " type=" + resolvedType + " args=" + service.getExtras()); if (caller != null) { @@ -10813,7 +11337,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } ServiceRecord r = res.record; if (unscheduleServiceRestartLocked(r)) { - if (DEBUG_SERVICE) Log.v(TAG, "START SERVICE WHILE RESTART PENDING: " + if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r.shortName); } r.startRequested = true; @@ -10871,7 +11395,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { - if (DEBUG_SERVICE) Log.v(TAG, "stopService: " + service + if (DEBUG_SERVICE) Slog.v(TAG, "stopService: " + service + " type=" + resolvedType); final ProcessRecord callerApp = getRecordForAppLocked(caller); @@ -10936,7 +11460,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public boolean stopServiceToken(ComponentName className, IBinder token, int startId) { synchronized(this) { - if (DEBUG_SERVICE) Log.v(TAG, "stopServiceToken: " + className + if (DEBUG_SERVICE) Slog.v(TAG, "stopServiceToken: " + className + " " + token + " startId=" + startId); ServiceRecord r = findServiceLocked(className, token); if (r != null) { @@ -10958,7 +11482,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (r.deliveredStarts.size() > 0) { - Log.w(TAG, "stopServiceToken startId " + startId + Slog.w(TAG, "stopServiceToken startId " + startId + " is last, but have " + r.deliveredStarts.size() + " remaining args"); } @@ -11004,6 +11528,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.isForeground) { r.isForeground = false; if (r.app != null) { + updateLruProcessLocked(r.app, false, true); updateServiceForegroundLocked(r.app, true); } } @@ -11045,7 +11570,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { - if (DEBUG_SERVICE) Log.v(TAG, "bindService: " + service + if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service + " type=" + resolvedType + " conn=" + connection.asBinder() + " flags=0x" + Integer.toHexString(flags)); final ProcessRecord callerApp = getRecordForAppLocked(caller); @@ -11060,7 +11585,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (token != null) { int aindex = indexOfTokenLocked(token); if (aindex < 0) { - Log.w(TAG, "Binding with unknown activity: " + token); + Slog.w(TAG, "Binding with unknown activity: " + token); return 0; } activity = (HistoryRecord)mHistory.get(aindex); @@ -11103,7 +11628,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); if (unscheduleServiceRestartLocked(s)) { - if (DEBUG_SERVICE) Log.v(TAG, "BIND SERVICE WHILE RESTART PENDING: " + if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: " + s.shortName); } @@ -11135,7 +11660,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen updateOomAdjLocked(s.app); } - if (DEBUG_SERVICE) Log.v(TAG, "Bind " + s + " with " + b + if (DEBUG_SERVICE) Slog.v(TAG, "Bind " + s + " with " + b + ": received=" + b.intent.received + " apps=" + b.intent.apps.size() + " doRebind=" + b.intent.doRebind); @@ -11146,7 +11671,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { c.conn.connected(s.name, b.intent.binder); } catch (Exception e) { - Log.w(TAG, "Failure sending service " + s.shortName + Slog.w(TAG, "Failure sending service " + s.shortName + " to connection " + c.conn.asBinder() + " (in " + c.binding.client.processName + ")", e); } @@ -11188,7 +11713,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen b.intent.apps.remove(b.client); } - if (DEBUG_SERVICE) Log.v(TAG, "Disconnecting binding " + b.intent + if (DEBUG_SERVICE) Slog.v(TAG, "Disconnecting binding " + b.intent + ": shouldUnbind=" + b.intent.hasBound); if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0 && b.intent.hasBound) { @@ -11201,7 +11726,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen b.intent.doRebind = false; s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent()); } catch (Exception e) { - Log.w(TAG, "Exception when unbinding service " + s.shortName, e); + Slog.w(TAG, "Exception when unbinding service " + s.shortName, e); serviceDoneExecutingLocked(s, true); } } @@ -11214,10 +11739,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public boolean unbindService(IServiceConnection connection) { synchronized (this) { IBinder binder = connection.asBinder(); - if (DEBUG_SERVICE) Log.v(TAG, "unbindService: conn=" + binder); + if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder); ConnectionRecord r = mServiceConnections.get(binder); if (r == null) { - Log.w(TAG, "Unbind failed: could not find connection for " + Slog.w(TAG, "Unbind failed: could not find connection for " + connection.asBinder()); return false; } @@ -11251,7 +11776,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); - if (DEBUG_SERVICE) Log.v(TAG, "PUBLISHING SERVICE " + r.name + if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING SERVICE " + r.name + " " + intent + ": " + service); if (r != null) { Intent.FilterComparison filter @@ -11267,19 +11792,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen while (it.hasNext()) { ConnectionRecord c = it.next(); if (!filter.equals(c.binding.intent.intent)) { - if (DEBUG_SERVICE) Log.v( + if (DEBUG_SERVICE) Slog.v( TAG, "Not publishing to: " + c); - if (DEBUG_SERVICE) Log.v( + if (DEBUG_SERVICE) Slog.v( TAG, "Bound intent: " + c.binding.intent.intent); - if (DEBUG_SERVICE) Log.v( + if (DEBUG_SERVICE) Slog.v( TAG, "Published intent: " + intent); continue; } - if (DEBUG_SERVICE) Log.v(TAG, "Publishing to: " + c); + if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c); try { c.conn.connected(r.name, service); } catch (Exception e) { - Log.w(TAG, "Failure sending service " + r.name + + Slog.w(TAG, "Failure sending service " + r.name + " to connection " + c.conn.asBinder() + " (in " + c.binding.client.processName + ")", e); } @@ -11312,7 +11837,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Intent.FilterComparison filter = new Intent.FilterComparison(intent); IntentBindRecord b = r.bindings.get(filter); - if (DEBUG_SERVICE) Log.v(TAG, "unbindFinished in " + r + if (DEBUG_SERVICE) Slog.v(TAG, "unbindFinished in " + r + " at " + b + ": apps=" + (b != null ? b.apps.size() : 0)); if (b != null) { @@ -11342,11 +11867,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ServiceRecord r = (ServiceRecord)token; boolean inStopping = mStoppingServices.contains(token); if (r != null) { - if (DEBUG_SERVICE) Log.v(TAG, "DONE EXECUTING SERVICE " + r.name + if (DEBUG_SERVICE) Slog.v(TAG, "DONE EXECUTING SERVICE " + r.name + ": nesting=" + r.executeNesting + ", inStopping=" + inStopping); if (r != token) { - Log.w(TAG, "Done executing service " + r.name + Slog.w(TAG, "Done executing service " + r.name + " with incorrect token: given " + token + ", expected " + r); return; @@ -11401,7 +11926,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen serviceDoneExecutingLocked(r, inStopping); Binder.restoreCallingIdentity(origId); } else { - Log.w(TAG, "Done executing unknown service " + r.name + Slog.w(TAG, "Done executing unknown service " + r.name + " with token " + token); } } @@ -11422,6 +11947,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } void serviceTimeout(ProcessRecord proc) { + String anrMessage = null; + synchronized(this) { if (proc.executingServices.size() == 0 || proc.thread == null) { return; @@ -11440,16 +11967,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen nextTime = sr.executingStart; } } - if (timeout != null && mLRUProcesses.contains(proc)) { - Log.w(TAG, "Timeout executing service: " + timeout); - appNotRespondingLocked(proc, null, null, "Executing service " - + timeout.name); + if (timeout != null && mLruProcesses.contains(proc)) { + Slog.w(TAG, "Timeout executing service: " + timeout); + anrMessage = "Executing service " + timeout.shortName; } else { Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG); msg.obj = proc; mHandler.sendMessageAtTime(msg, nextTime+SERVICE_TIMEOUT); } } + + if (anrMessage != null) { + appNotResponding(proc, null, null, anrMessage); + } } // ========================================================= @@ -11460,7 +11990,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // instantiated. The backup agent will invoke backupAgentCreated() on the // activity manager to announce its creation. public boolean bindBackupAgent(ApplicationInfo app, int backupMode) { - if (DEBUG_BACKUP) Log.v(TAG, "startBackupAgent: app=" + app + " mode=" + backupMode); + if (DEBUG_BACKUP) Slog.v(TAG, "startBackupAgent: app=" + app + " mode=" + backupMode); enforceCallingPermission("android.permission.BACKUP", "startBackupAgent"); synchronized(this) { @@ -11477,7 +12007,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ProcessRecord proc = startProcessLocked(app.processName, app, false, 0, "backup", hostingName, false); if (proc == null) { - Log.e(TAG, "Unable to start backup agent process " + r); + Slog.e(TAG, "Unable to start backup agent process " + r); return false; } @@ -11491,14 +12021,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If the process is already attached, schedule the creation of the backup agent now. // If it is not yet live, this will be done when it attaches to the framework. if (proc.thread != null) { - if (DEBUG_BACKUP) Log.v(TAG, "Agent proc already running: " + proc); + if (DEBUG_BACKUP) Slog.v(TAG, "Agent proc already running: " + proc); try { proc.thread.scheduleCreateBackupAgent(app, backupMode); } catch (RemoteException e) { // Will time out on the backup manager side } } else { - if (DEBUG_BACKUP) Log.v(TAG, "Agent proc not running, waiting for attach"); + if (DEBUG_BACKUP) Slog.v(TAG, "Agent proc not running, waiting for attach"); } // Invariants: at this point, the target app process exists and the application // is either already running or in the process of coming up. mBackupTarget and @@ -11511,12 +12041,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // A backup agent has just come up public void backupAgentCreated(String agentPackageName, IBinder agent) { - if (DEBUG_BACKUP) Log.v(TAG, "backupAgentCreated: " + agentPackageName + if (DEBUG_BACKUP) Slog.v(TAG, "backupAgentCreated: " + agentPackageName + " = " + agent); synchronized(this) { if (!agentPackageName.equals(mBackupAppName)) { - Log.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!"); + Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!"); return; } @@ -11528,7 +12058,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { // can't happen; the backup manager service is local } catch (Exception e) { - Log.w(TAG, "Exception trying to deliver BackupAgent binding: "); + Slog.w(TAG, "Exception trying to deliver BackupAgent binding: "); e.printStackTrace(); } finally { Binder.restoreCallingIdentity(oldIdent); @@ -11538,20 +12068,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // done with this agent public void unbindBackupAgent(ApplicationInfo appInfo) { - if (DEBUG_BACKUP) Log.v(TAG, "unbindBackupAgent: " + appInfo); + if (DEBUG_BACKUP) Slog.v(TAG, "unbindBackupAgent: " + appInfo); if (appInfo == null) { - Log.w(TAG, "unbind backup agent for null app"); + Slog.w(TAG, "unbind backup agent for null app"); return; } synchronized(this) { if (mBackupAppName == null) { - Log.w(TAG, "Unbinding backup agent with no active backup"); + Slog.w(TAG, "Unbinding backup agent with no active backup"); return; } if (!mBackupAppName.equals(appInfo.packageName)) { - Log.e(TAG, "Unbind of " + appInfo + " but is not the current backup target"); + Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target"); return; } @@ -11567,7 +12097,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { proc.thread.scheduleDestroyBackupAgent(appInfo); } catch (Exception e) { - Log.e(TAG, "Exception when unbinding backup agent:"); + Slog.e(TAG, "Exception when unbinding backup agent:"); e.printStackTrace(); } } @@ -11598,7 +12128,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private final void scheduleBroadcastsLocked() { - if (DEBUG_BROADCAST) Log.v(TAG, "Schedule broadcasts: current=" + if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts: current=" + mBroadcastsScheduled); if (mBroadcastsScheduled) { @@ -11639,7 +12169,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // the client. Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null; - if (DEBUG_BROADCAST) Log.v(TAG, "Register receiver " + filter + if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter + ": " + sticky); if (receiver == null) { @@ -11667,7 +12197,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen BroadcastFilter bf = new BroadcastFilter(filter, rl, permission); rl.add(bf); if (!bf.debugCheck()) { - Log.w(TAG, "==> For Dynamic broadast"); + Slog.w(TAG, "==> For Dynamic broadast"); } mReceiverResolver.addFilter(bf); @@ -11695,7 +12225,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } public void unregisterReceiver(IIntentReceiver receiver) { - if (DEBUG_BROADCAST) Log.v(TAG, "Unregister receiver: " + receiver); + if (DEBUG_BROADCAST) Slog.v(TAG, "Unregister receiver: " + receiver); boolean doNext = false; @@ -11746,11 +12276,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean ordered, boolean sticky, int callingPid, int callingUid) { intent = new Intent(intent); - if (DEBUG_BROADCAST_LIGHT) Log.v( + if (DEBUG_BROADCAST_LIGHT) Slog.v( TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent + " ordered=" + ordered); if ((resultTo != null) && !ordered) { - Log.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!"); + Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!"); } // Handle special intents: if this broadcast is from the package @@ -11760,6 +12290,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen intent.getAction()); if (intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction()) || intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction()) + || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction()) || uidRemoved) { if (checkComponentPermission( android.Manifest.permission.BROADCAST_PACKAGE_REMOVED, @@ -11776,15 +12307,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } } else { - Uri data = intent.getData(); - String ssp; - if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { - if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) { - uninstallPackageLocked(ssp, - intent.getIntExtra(Intent.EXTRA_UID, -1), false); - AttributeCache ac = AttributeCache.instance(); - if (ac != null) { - ac.removePackage(ssp); + // If resources are unvailble just force stop all + // those packages and flush the attribute cache as well. + if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) { + String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + if (list != null && (list.length > 0)) { + for (String pkg : list) { + forceStopPackageLocked(pkg, -1, false, true, true); + } + } + } else { + Uri data = intent.getData(); + String ssp; + if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { + if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) { + forceStopPackageLocked(ssp, + intent.getIntExtra(Intent.EXTRA_UID, -1), false, true, true); } } } @@ -11795,7 +12333,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + ", uid=" + callingUid + ")" + " requires " + android.Manifest.permission.BROADCAST_PACKAGE_REMOVED; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } } @@ -11823,11 +12361,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen String msg = "Permission Denial: not allowed to send broadcast " + intent.getAction() + " from pid=" + callingPid + ", uid=" + callingUid; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } } catch (RemoteException e) { - Log.w(TAG, "Remote exception", e); + Slog.w(TAG, "Remote exception", e); return BROADCAST_SUCCESS; } } @@ -11840,11 +12378,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid=" + callingPid + ", uid=" + callingUid + " requires " + android.Manifest.permission.BROADCAST_STICKY; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } if (requiredPermission != null) { - Log.w(TAG, "Can't broadcast sticky intent " + intent + Slog.w(TAG, "Can't broadcast sticky intent " + intent + " and enforce permission " + requiredPermission); return BROADCAST_STICKY_CANT_HAVE_PERMISSION; } @@ -11899,6 +12437,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // pm is in same process, this will never happen. } + final boolean replacePending = + (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0; + + if (DEBUG_BROADCAST) Slog.v(TAG, "Enqueing broadcast: " + intent.getAction() + + " replacePending=" + replacePending); + int NR = registeredReceivers != null ? registeredReceivers.size() : 0; if (!ordered && NR > 0) { // If we are not serializing this broadcast, then send the @@ -11908,11 +12452,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen callerPackage, callingPid, callingUid, requiredPermission, registeredReceivers, resultTo, resultCode, resultData, map, ordered, sticky, false); - if (DEBUG_BROADCAST) Log.v( + if (DEBUG_BROADCAST) Slog.v( TAG, "Enqueueing parallel broadcast " + r + ": prev had " + mParallelBroadcasts.size()); - mParallelBroadcasts.add(r); - scheduleBroadcastsLocked(); + boolean replaced = false; + if (replacePending) { + for (int i=mParallelBroadcasts.size()-1; i>=0; i--) { + if (intent.filterEquals(mParallelBroadcasts.get(i).intent)) { + if (DEBUG_BROADCAST) Slog.v(TAG, + "***** DROPPING PARALLEL: " + intent); + mParallelBroadcasts.set(i, r); + replaced = true; + break; + } + } + } + if (!replaced) { + mParallelBroadcasts.add(r); + scheduleBroadcastsLocked(); + } registeredReceivers = null; NR = 0; } @@ -11926,25 +12484,32 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // installed. Maybe in the future we want to have a special install // broadcast or such for apps, but we'd like to deliberately make // this decision. - boolean skip = false; - if (intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) { - skip = true; - } else if (intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())) { - skip = true; - } else if (intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) { - skip = true; - } - String skipPackage = (skip && intent.getData() != null) - ? intent.getData().getSchemeSpecificPart() - : null; - if (skipPackage != null && receivers != null) { - int NT = receivers.size(); - for (int it=0; it<NT; it++) { - ResolveInfo curt = (ResolveInfo)receivers.get(it); - if (curt.activityInfo.packageName.equals(skipPackage)) { - receivers.remove(it); - it--; - NT--; + String skipPackages[] = null; + if (intent.ACTION_PACKAGE_ADDED.equals(intent.getAction()) + || intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction()) + || intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) { + Uri data = intent.getData(); + if (data != null) { + String pkgName = data.getSchemeSpecificPart(); + if (pkgName != null) { + skipPackages = new String[] { pkgName }; + } + } + } else if (intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) { + skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + } + if (skipPackages != null && (skipPackages.length > 0)) { + for (String skipPackage : skipPackages) { + if (skipPackage != null) { + int NT = receivers.size(); + for (int it=0; it<NT; it++) { + ResolveInfo curt = (ResolveInfo)receivers.get(it); + if (curt.activityInfo.packageName.equals(skipPackage)) { + receivers.remove(it); + it--; + NT--; + } + } } } } @@ -11988,15 +12553,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen callerPackage, callingPid, callingUid, requiredPermission, receivers, resultTo, resultCode, resultData, map, ordered, sticky, false); - if (DEBUG_BROADCAST) Log.v( + if (DEBUG_BROADCAST) Slog.v( TAG, "Enqueueing ordered broadcast " + r + ": prev had " + mOrderedBroadcasts.size()); if (DEBUG_BROADCAST) { int seq = r.intent.getIntExtra("seq", -1); - Log.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq); + Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq); + } + boolean replaced = false; + if (replacePending) { + for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) { + if (intent.filterEquals(mOrderedBroadcasts.get(i).intent)) { + if (DEBUG_BROADCAST) Slog.v(TAG, + "***** DROPPING ORDERED: " + intent); + mOrderedBroadcasts.set(i, r); + replaced = true; + break; + } + } + } + if (!replaced) { + mOrderedBroadcasts.add(r); + scheduleBroadcastsLocked(); } - mOrderedBroadcasts.add(r); - scheduleBroadcastsLocked(); } return BROADCAST_SUCCESS; @@ -12021,7 +12600,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 + Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent + " before boot completion"); throw new IllegalStateException("Cannot broadcast before boot completed"); } @@ -12074,7 +12653,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + android.Manifest.permission.BROADCAST_STICKY; - Log.w(TAG, msg); + Slog.w(TAG, msg); throw new SecurityException(msg); } ArrayList<Intent> list = mStickyBroadcasts.get(intent.getAction()); @@ -12096,26 +12675,26 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean explicit) { if (mOrderedBroadcasts.size() == 0) { if (explicit) { - Log.w(TAG, "finishReceiver called but no pending broadcasts"); + Slog.w(TAG, "finishReceiver called but no pending broadcasts"); } return false; } BroadcastRecord r = mOrderedBroadcasts.get(0); if (r.receiver == null) { if (explicit) { - Log.w(TAG, "finishReceiver called but none active"); + Slog.w(TAG, "finishReceiver called but none active"); } return false; } if (r.receiver != receiver) { - Log.w(TAG, "finishReceiver called but active receiver is different"); + Slog.w(TAG, "finishReceiver called but active receiver is different"); return false; } int state = r.state; r.state = r.IDLE; if (state == r.IDLE) { if (explicit) { - Log.w(TAG, "finishReceiver called but state is IDLE"); + Slog.w(TAG, "finishReceiver called but state is IDLE"); } } r.receiver = null; @@ -12146,7 +12725,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle resultExtras, boolean resultAbort) { - if (DEBUG_BROADCAST) Log.v(TAG, "Finish receiver: " + who); + if (DEBUG_BROADCAST) Slog.v(TAG, "Finish receiver: " + who); // Refuse possible leaked file descriptors if (resultExtras != null && resultExtras.hasFileDescriptors()) { @@ -12175,22 +12754,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Object curReceiver = r.receivers.get(r.nextReceiver-1); if (curReceiver instanceof BroadcastFilter) { BroadcastFilter bf = (BroadcastFilter) curReceiver; - EventLog.writeEvent(LOG_AM_BROADCAST_DISCARD_FILTER, + EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER, System.identityHashCode(r), r.intent.getAction(), r.nextReceiver - 1, System.identityHashCode(bf)); } else { - EventLog.writeEvent(LOG_AM_BROADCAST_DISCARD_APP, + EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP, System.identityHashCode(r), r.intent.getAction(), r.nextReceiver - 1, ((ResolveInfo)curReceiver).toString()); } } else { - Log.w(TAG, "Discarding broadcast before first receiver is invoked: " + Slog.w(TAG, "Discarding broadcast before first receiver is invoked: " + r); - EventLog.writeEvent(LOG_AM_BROADCAST_DISCARD_APP, + EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP, System.identityHashCode(r), r.intent.getAction(), r.nextReceiver, @@ -12199,6 +12778,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private final void broadcastTimeout() { + ProcessRecord app = null; + String anrMessage = null; + synchronized (this) { if (mOrderedBroadcasts.size() == 0) { return; @@ -12206,7 +12788,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen long now = SystemClock.uptimeMillis(); BroadcastRecord r = mOrderedBroadcasts.get(0); if ((r.receiverTime+BROADCAST_TIMEOUT) > now) { - if (DEBUG_BROADCAST) Log.v(TAG, + if (DEBUG_BROADCAST) Slog.v(TAG, "Premature timeout @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for " + (r.receiverTime + BROADCAST_TIMEOUT)); Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG); @@ -12214,20 +12796,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } - Log.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver); + Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver); r.receiverTime = now; r.anrCount++; // Current receiver has passed its expiration date. if (r.nextReceiver <= 0) { - Log.w(TAG, "Timeout on receiver with nextReceiver <= 0"); + Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0"); return; } - ProcessRecord app = null; - Object curReceiver = r.receivers.get(r.nextReceiver-1); - Log.w(TAG, "Receiver during timeout: " + curReceiver); + Slog.w(TAG, "Receiver during timeout: " + curReceiver); logBroadcastReceiverDiscard(r); if (curReceiver instanceof BroadcastFilter) { BroadcastFilter bf = (BroadcastFilter)curReceiver; @@ -12243,8 +12823,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (app != null) { - appNotRespondingLocked(app, null, null, - "Broadcast of " + r.intent.toString()); + anrMessage = "Broadcast of " + r.intent.toString(); } if (mPendingBroadcast == r) { @@ -12256,6 +12835,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.resultExtras, r.resultAbort, true); scheduleBroadcastsLocked(); } + + if (anrMessage != null) { + appNotResponding(app, null, null, anrMessage); + } } private final void processCurBroadcastLocked(BroadcastRecord r, @@ -12266,14 +12849,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.receiver = app.thread.asBinder(); r.curApp = app; app.curReceiver = r; - updateLRUListLocked(app, true); + updateLruProcessLocked(app, true, true); // Tell the application to launch this receiver. r.intent.setComponent(r.curComponent); boolean started = false; try { - if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, + if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Delivering to component " + r.curComponent + ": " + r); ensurePackageDexOpt(r.intent.getComponent().getPackageName()); @@ -12310,7 +12893,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int perm = checkComponentPermission(filter.requiredPermission, r.callingPid, r.callingUid, -1); if (perm != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Permission Denial: broadcasting " + Slog.w(TAG, "Permission Denial: broadcasting " + r.intent.toString() + " from " + r.callerPackage + " (pid=" + r.callingPid + ", uid=" + r.callingUid + ")" @@ -12323,7 +12906,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int perm = checkComponentPermission(r.requiredPermission, filter.receiverList.pid, filter.receiverList.uid, -1); if (perm != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Permission Denial: receiving " + Slog.w(TAG, "Permission Denial: receiving " + r.intent.toString() + " to " + filter.receiverList.app + " (pid=" + filter.receiverList.pid @@ -12358,7 +12941,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { if (DEBUG_BROADCAST_LIGHT) { int seq = r.intent.getIntExtra("seq", -1); - Log.i(TAG, "Delivering to " + filter.receiverList.app + Slog.i(TAG, "Delivering to " + filter + " (seq=" + seq + "): " + r); } performReceive(filter.receiverList.app, filter.receiverList.receiver, @@ -12368,7 +12951,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.state = BroadcastRecord.CALL_DONE_RECEIVE; } } catch (RemoteException e) { - Log.w(TAG, "Failure sending broadcast " + r.intent, e); + Slog.w(TAG, "Failure sending broadcast " + r.intent, e); if (ordered) { r.receiver = null; r.curFilter = null; @@ -12396,9 +12979,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized(this) { BroadcastRecord r; - if (DEBUG_BROADCAST) Log.v(TAG, "processNextBroadcast: " + if (DEBUG_BROADCAST) Slog.v(TAG, "processNextBroadcast: " + mParallelBroadcasts.size() + " broadcasts, " - + mOrderedBroadcasts.size() + " serialized broadcasts"); + + mOrderedBroadcasts.size() + " ordered broadcasts"); updateCpuStats(); @@ -12411,17 +12994,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r = mParallelBroadcasts.remove(0); r.dispatchTime = SystemClock.uptimeMillis(); final int N = r.receivers.size(); - if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Processing parallel broadcast " + if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast " + r); for (int i=0; i<N; i++) { Object target = r.receivers.get(i); - if (DEBUG_BROADCAST) Log.v(TAG, - "Delivering non-serialized to registered " + if (DEBUG_BROADCAST) Slog.v(TAG, + "Delivering non-ordered to registered " + target + ": " + r); deliverToRegisteredReceiver(r, (BroadcastFilter)target, false); } addBroadcastToHistoryLocked(r); - if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Done with parallel broadcast " + if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast " + r); } @@ -12432,7 +13015,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // check that the process we're waiting for still exists. if (mPendingBroadcast != null) { if (DEBUG_BROADCAST_LIGHT) { - Log.v(TAG, "processNextBroadcast: waiting for " + Slog.v(TAG, "processNextBroadcast: waiting for " + mPendingBroadcast.curApp); } @@ -12444,7 +13027,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // It's still alive, so keep waiting return; } else { - Log.w(TAG, "pending app " + mPendingBroadcast.curApp + Slog.w(TAG, "pending app " + mPendingBroadcast.curApp + " died before responding to broadcast"); mPendingBroadcast = null; } @@ -12475,7 +13058,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.dispatchTime > 0) { if ((numReceivers > 0) && (now > r.dispatchTime + (2*BROADCAST_TIMEOUT*numReceivers))) { - Log.w(TAG, "Hung broadcast discarded after timeout failure:" + Slog.w(TAG, "Hung broadcast discarded after timeout failure:" + " now=" + now + " dispatchTime=" + r.dispatchTime + " startTime=" + r.receiverTime @@ -12490,7 +13073,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (r.state != BroadcastRecord.IDLE) { - if (DEBUG_BROADCAST) Log.d(TAG, + if (DEBUG_BROADCAST) Slog.d(TAG, "processNextBroadcast() called when not idle (state=" + r.state + ")"); return; @@ -12504,21 +13087,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { if (DEBUG_BROADCAST) { int seq = r.intent.getIntExtra("seq", -1); - Log.i(TAG, "Finishing broadcast " + r.intent.getAction() + Slog.i(TAG, "Finishing broadcast " + r.intent.getAction() + " seq=" + seq + " app=" + r.callerApp); } performReceive(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, false, false); } catch (RemoteException e) { - Log.w(TAG, "Failure sending broadcast result of " + r.intent, e); + Slog.w(TAG, "Failure sending broadcast result of " + r.intent, e); } } - if (DEBUG_BROADCAST) Log.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG"); + if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG"); mHandler.removeMessages(BROADCAST_TIMEOUT_MSG); - if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Finished with ordered broadcast " + if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast " + r); // ... and on to the next... @@ -12539,9 +13122,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (recIdx == 0) { r.dispatchTime = r.receiverTime; - if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Processing ordered broadcast " + if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast " + r); - if (DEBUG_BROADCAST) Log.v(TAG, + if (DEBUG_BROADCAST) Slog.v(TAG, "Submitting BROADCAST_TIMEOUT_MSG for " + (r.receiverTime + BROADCAST_TIMEOUT)); Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG); @@ -12553,13 +13136,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Simple case: this is a registered receiver who gets // a direct call. BroadcastFilter filter = (BroadcastFilter)nextReceiver; - if (DEBUG_BROADCAST) Log.v(TAG, - "Delivering serialized to registered " + if (DEBUG_BROADCAST) Slog.v(TAG, + "Delivering ordered to registered " + filter + ": " + r); deliverToRegisteredReceiver(r, filter, r.ordered); if (r.receiver == null || !r.ordered) { // The receiver has already finished, so schedule to // process the next one. + if (DEBUG_BROADCAST) Slog.v(TAG, "Quick finishing: ordered=" + + r.ordered + " receiver=" + r.receiver); r.state = BroadcastRecord.IDLE; scheduleBroadcastsLocked(); } @@ -12578,7 +13163,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen info.activityInfo.exported ? -1 : info.activityInfo.applicationInfo.uid); if (perm != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Permission Denial: broadcasting " + Slog.w(TAG, "Permission Denial: broadcasting " + r.intent.toString() + " from " + r.callerPackage + " (pid=" + r.callingPid + ", uid=" + r.callingUid + ")" @@ -12597,7 +13182,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen perm = PackageManager.PERMISSION_DENIED; } if (perm != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Permission Denial: receiving " + Slog.w(TAG, "Permission Denial: receiving " + r.intent + " to " + info.activityInfo.applicationInfo.packageName + " requires " + r.requiredPermission @@ -12634,7 +13219,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen processCurBroadcastLocked(r, app); return; } catch (RemoteException e) { - Log.w(TAG, "Exception when sending broadcast to " + Slog.w(TAG, "Exception when sending broadcast to " + r.curComponent, e); } @@ -12651,7 +13236,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen == null) { // Ah, this recipient is unavailable. Finish it if necessary, // and mark the broadcast record as ready for the next. - Log.w(TAG, "Unable to launch app " + Slog.w(TAG, "Unable to launch app " + info.activityInfo.applicationInfo.packageName + "/" + info.activityInfo.applicationInfo.uid + " for broadcast " + r.intent + ": process is bad"); @@ -12715,7 +13300,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } final long origId = Binder.clearCallingIdentity(); - uninstallPackageLocked(ii.targetPackage, -1, true); + forceStopPackageLocked(ii.targetPackage, -1, true, false, true); ProcessRecord app = addAppLocked(ai); app.instrumentationClass = className; app.instrumentationInfo = ai; @@ -12740,7 +13325,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ private void reportStartInstrumentationFailure(IInstrumentationWatcher watcher, ComponentName cn, String report) { - Log.w(TAG, report); + Slog.w(TAG, report); try { if (watcher != null) { Bundle results = new Bundle(); @@ -12749,7 +13334,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen watcher.instrumentationStatus(cn, -1, results); } } catch (RemoteException e) { - Log.w(TAG, e); + Slog.w(TAG, e); } } @@ -12770,7 +13355,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.instrumentationProfileFile = null; app.instrumentationArguments = null; - uninstallPackageLocked(app.processName, -1, false); + forceStopPackageLocked(app.processName, -1, false, false, true); } public void finishInstrumentation(IApplicationThread target, @@ -12783,7 +13368,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized(this) { ProcessRecord app = getRecordForAppLocked(target); if (app == null) { - Log.w(TAG, "finishInstrumentation: no app for " + target); + Slog.w(TAG, "finishInstrumentation: no app for " + target); return; } final long origId = Binder.clearCallingIdentity(); @@ -12857,10 +13442,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen changes = newConfig.updateFrom(values); if (changes != 0) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) { - Log.i(TAG, "Updating configuration to: " + values); + Slog.i(TAG, "Updating configuration to: " + values); } - EventLog.writeEvent(LOG_CONFIGURATION_CHANGED, changes); + EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes); if (values.locale != null) { saveLocaleLocked(values.locale, @@ -12868,24 +13453,30 @@ public final class ActivityManagerService extends ActivityManagerNative implemen values.userSetLocale); } + mConfigurationSeq++; + if (mConfigurationSeq <= 0) { + mConfigurationSeq = 1; + } + newConfig.seq = mConfigurationSeq; mConfiguration = newConfig; - Log.i(TAG, "Config changed: " + newConfig); + Slog.i(TAG, "Config changed: " + newConfig); AttributeCache ac = AttributeCache.instance(); if (ac != null) { ac.updateConfiguration(mConfiguration); } - Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG); - msg.obj = new Configuration(mConfiguration); - mHandler.sendMessage(msg); + if (Settings.System.hasInterestingConfigurationChanges(changes)) { + Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG); + msg.obj = new Configuration(mConfiguration); + mHandler.sendMessage(msg); + } - final int N = mLRUProcesses.size(); - for (int i=0; i<N; i++) { - ProcessRecord app = mLRUProcesses.get(i); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); try { if (app.thread != null) { - if (DEBUG_CONFIGURATION) Log.v(TAG, "Sending to proc " + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " + app.processName + " new config " + mConfiguration); app.thread.scheduleConfigurationChanged(mConfiguration); } @@ -12893,7 +13484,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_REPLACE_PENDING); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, false, false, MY_PID, Process.SYSTEM_UID); if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) { @@ -12918,12 +13510,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If this didn't result in the starting activity being // destroyed, then we need to make sure at this point that all // other activities are made visible. - if (DEBUG_SWITCH) Log.i(TAG, "Config didn't destroy " + starting + if (DEBUG_SWITCH) Slog.i(TAG, "Config didn't destroy " + starting + ", ensuring others are correct."); ensureActivitiesVisibleLocked(starting, changes); } } + if (values != null && mWindowManager != null) { + mWindowManager.setNewConfiguration(mConfiguration); + } + return kept; } @@ -12935,17 +13531,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen results = r.results; newIntents = r.newIntents; } - if (DEBUG_SWITCH) Log.v(TAG, "Relaunching: " + r + if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r + " with results=" + results + " newIntents=" + newIntents + " andResume=" + andResume); - EventLog.writeEvent(andResume ? LOG_AM_RELAUNCH_RESUME_ACTIVITY - : LOG_AM_RELAUNCH_ACTIVITY, System.identityHashCode(r), + EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY + : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r), r.task.taskId, r.shortComponentName); r.startFreezingScreenLocked(r.app, 0); try { - if (DEBUG_SWITCH) Log.i(TAG, "Switch is restarting resumed " + r); + if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r); r.app.thread.scheduleRelaunchActivity(r, results, newIntents, changes, !andResume, mConfiguration); // Note: don't need to call pauseIfSleepingLocked() here, because @@ -12973,21 +13569,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ private final boolean ensureActivityConfigurationLocked(HistoryRecord r, int globalChanges) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG, + if (mConfigWillChange) { + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, + "Skipping config check (will change): " + r); + return true; + } + + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Ensuring correct configuration: " + r); // Short circuit: if the two configurations are the exact same // object (the common case), then there is nothing to do. Configuration newConfig = mConfiguration; if (r.configuration == newConfig) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG, + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration unchanged in " + r); return true; } // We don't worry about activities that are finishing. if (r.finishing) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG, + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration doesn't matter in finishing " + r); r.stopFreezingScreenLocked(false); return true; @@ -13001,7 +13603,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If the activity isn't currently running, just leave the new // configuration and it will pick that up next time it starts. if (r.app == null || r.app.thread == null) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG, + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration doesn't matter not running " + r); r.stopFreezingScreenLocked(false); return true; @@ -13014,7 +13616,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Figure out what has changed between the two configurations. int changes = oldConfig.diff(newConfig); if (DEBUG_SWITCH || DEBUG_CONFIGURATION) { - Log.v(TAG, "Checking to restart " + r.info.name + ": changed=0x" + Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x" + Integer.toHexString(changes) + ", handles=0x" + Integer.toHexString(r.info.configChanges) + ", newConfig=" + newConfig); @@ -13024,14 +13626,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.configChangeFlags |= changes; r.startFreezingScreenLocked(r.app, globalChanges); if (r.app == null || r.app.thread == null) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG, + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Switch is destroying non-running " + r); destroyActivityLocked(r, true); } else if (r.state == ActivityState.PAUSING) { // A little annoying: we are waiting for this activity to // finish pausing. Let's not do anything now, but just // flag that it needs to be restarted when done pausing. - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG, + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Switch is skipping already pausing " + r); r.configDestroy = true; return true; @@ -13040,12 +13642,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // and we need to restart the top, resumed activity. // Instead of doing the normal handshaking, just say // "restart!". - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG, + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Switch is restarting resumed " + r); relaunchActivityLocked(r, r.configChangeFlags, true); r.configChangeFlags = 0; } else { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG, + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Switch is restarting non-resumed " + r); relaunchActivityLocked(r, r.configChangeFlags, false); r.configChangeFlags = 0; @@ -13064,7 +13666,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // it last got. if (r.app != null && r.app.thread != null) { try { - if (DEBUG_CONFIGURATION) Log.v(TAG, "Sending new config to " + r); + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r); r.app.thread.scheduleActivityConfigurationChanged(r); } catch (RemoteException e) { // If process died, whatever. @@ -13095,15 +13697,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // LIFETIME MANAGEMENT // ========================================================= - private final int computeOomAdjLocked( - ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) { + private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj, + ProcessRecord TOP_APP, boolean recursed) { if (mAdjSeq == app.adjSeq) { - // This adjustment has already been computed. + // This adjustment has already been computed. If we are calling + // from the top, we may have already computed our adjustment with + // an earlier hidden adjustment that isn't really for us... if + // so, use the new hidden adjustment. + if (!recursed && app.hidden) { + app.curAdj = hiddenAdj; + } return app.curAdj; } if (app.thread == null) { app.adjSeq = mAdjSeq; + app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; return (app.curAdj=EMPTY_APP_ADJ); } @@ -13120,84 +13729,107 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN; app.adjSource = null; app.adjTarget = null; + app.empty = false; + app.hidden = false; // Determine the importance of the process, starting with most // important to least, and assign an appropriate OOM adjustment. int adj; + int schedGroup; int N; if (app == TOP_APP) { // The last app on the list is the foreground app. adj = FOREGROUND_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; app.adjType = "top-activity"; } else if (app.instrumentationClass != null) { // Don't want to kill running instrumentation. adj = FOREGROUND_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; app.adjType = "instrumentation"; } else if (app.persistentActivities > 0) { // Special persistent activities... shouldn't be used these days. adj = FOREGROUND_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; app.adjType = "persistent"; } else if (app.curReceiver != null || (mPendingBroadcast != null && mPendingBroadcast.curApp == app)) { // An app that is currently receiving a broadcast also // counts as being in the foreground. adj = FOREGROUND_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; app.adjType = "broadcast"; } else if (app.executingServices.size() > 0) { // An app that is currently executing a service callback also // counts as being in the foreground. adj = FOREGROUND_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; app.adjType = "exec-service"; } else if (app.foregroundServices) { // The user is aware of this app, so make it visible. adj = VISIBLE_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; app.adjType = "foreground-service"; } else if (app.forcingToForeground != null) { // The user is aware of this app, so make it visible. adj = VISIBLE_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; app.adjType = "force-foreground"; app.adjSource = app.forcingToForeground; } 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; + schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; app.adjType = "home"; } else if ((N=app.activities.size()) != 0) { // This app is in the background with paused activities. + app.hidden = true; adj = hiddenAdj; + schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; app.adjType = "bg-activities"; + N = app.activities.size(); for (int j=0; j<N; j++) { if (((HistoryRecord)app.activities.get(j)).visible) { // This app has a visible activity! + app.hidden = false; adj = VISIBLE_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; app.adjType = "visible"; break; } } } else { - // A very not-needed process. - adj = EMPTY_APP_ADJ; - app.adjType = "empty"; + // A very not-needed process. If this is lower in the lru list, + // we will push it in to the empty bucket. + app.hidden = true; + app.empty = true; + schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + adj = hiddenAdj; + app.adjType = "bg-empty"; } + //Slog.i(TAG, "OOM " + app + ": initial adj=" + adj); + // 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. app.adjSeq = mAdjSeq; app.curRawAdj = adj; - app.curAdj = adj <= app.maxAdj ? adj : app.maxAdj; if (mBackupTarget != null && app == mBackupTarget.app) { // If possible we want to avoid killing apps while they're being backed up if (adj > BACKUP_APP_ADJ) { - if (DEBUG_BACKUP) Log.v(TAG, "oom BACKUP_APP_ADJ for " + app); + if (DEBUG_BACKUP) Slog.v(TAG, "oom BACKUP_APP_ADJ for " + app); adj = BACKUP_APP_ADJ; app.adjType = "backup"; + app.hidden = false; } } - if (app.services.size() != 0 && adj > FOREGROUND_APP_ADJ) { + if (app.services.size() != 0 && (adj > FOREGROUND_APP_ADJ + || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) { final long now = SystemClock.uptimeMillis(); // This process is more important if the top activity is // bound to the service. @@ -13212,10 +13844,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (adj > SECONDARY_SERVER_ADJ) { adj = SECONDARY_SERVER_ADJ; app.adjType = "started-services"; + app.hidden = false; } } + // If we have let the service slide into the background + // state, still have some text describing what it is doing + // even though the service no longer has an impact. + if (adj > SECONDARY_SERVER_ADJ) { + app.adjType = "started-bg-services"; + } } - if (s.connections.size() > 0 && adj > FOREGROUND_APP_ADJ) { + if (s.connections.size() > 0 && (adj > FOREGROUND_APP_ADJ + || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) { Iterator<ConnectionRecord> kt = s.connections.values().iterator(); while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) { @@ -13237,25 +13877,35 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } int clientAdj = computeOomAdjLocked( - client, myHiddenAdj, TOP_APP); + client, myHiddenAdj, TOP_APP, true); if (adj > clientAdj) { adj = clientAdj > VISIBLE_APP_ADJ ? clientAdj : VISIBLE_APP_ADJ; + if (!client.hidden) { + app.hidden = false; + } app.adjType = "service"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_SERVICE_IN_USE; app.adjSource = cr.binding.client; app.adjTarget = s.serviceInfo.name; } + if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { + if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { + schedGroup = Process.THREAD_GROUP_DEFAULT; + } + } } HistoryRecord a = cr.activity; //if (a != null) { - // Log.i(TAG, "Connection to " + a ": state=" + a.state); + // Slog.i(TAG, "Connection to " + a ": state=" + a.state); //} if (a != null && adj > FOREGROUND_APP_ADJ && (a.state == ActivityState.RESUMED || a.state == ActivityState.PAUSING)) { adj = FOREGROUND_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; + app.hidden = false; app.adjType = "service"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_SERVICE_IN_USE; @@ -13273,13 +13923,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // its services we may bump it up from there. if (adj > hiddenAdj) { adj = hiddenAdj; + app.hidden = false; app.adjType = "bg-services"; } } - if (app.pubProviders.size() != 0 && adj > FOREGROUND_APP_ADJ) { + if (app.pubProviders.size() != 0 && (adj > FOREGROUND_APP_ADJ + || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) { Iterator jt = app.pubProviders.values().iterator(); - while (jt.hasNext() && adj > FOREGROUND_APP_ADJ) { + while (jt.hasNext() && (adj > FOREGROUND_APP_ADJ + || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) { ContentProviderRecord cpr = (ContentProviderRecord)jt.next(); if (cpr.clients.size() != 0) { Iterator<ProcessRecord> kt = cpr.clients.iterator(); @@ -13298,16 +13951,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } int clientAdj = computeOomAdjLocked( - client, myHiddenAdj, TOP_APP); + client, myHiddenAdj, TOP_APP, true); if (adj > clientAdj) { adj = clientAdj > FOREGROUND_APP_ADJ ? clientAdj : FOREGROUND_APP_ADJ; + if (!client.hidden) { + app.hidden = false; + } app.adjType = "provider"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_PROVIDER_IN_USE; app.adjSource = client; app.adjTarget = cpr.info.name; } + if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { + schedGroup = Process.THREAD_GROUP_DEFAULT; + } } } // If the provider has external (non-framework) process @@ -13316,34 +13975,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (cpr.externals != 0) { if (adj > FOREGROUND_APP_ADJ) { adj = FOREGROUND_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; + app.hidden = false; app.adjType = "provider"; app.adjTarget = cpr.info.name; } } } - - // Finally, if this process has published any content providers, - // then its adjustment makes it at least as important as any of the - // processes using those providers, and no less important than - // CONTENT_PROVIDER_ADJ, which is just shy of EMPTY. - if (adj > CONTENT_PROVIDER_ADJ) { - adj = CONTENT_PROVIDER_ADJ; - app.adjType = "pub-providers"; - } } app.curRawAdj = adj; - //Log.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid + + //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid + // " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj); if (adj > app.maxAdj) { adj = app.maxAdj; + if (app.maxAdj <= VISIBLE_APP_ADJ) { + schedGroup = Process.THREAD_GROUP_DEFAULT; + } } app.curAdj = adj; - app.curSchedGroup = adj > VISIBLE_APP_ADJ - ? Process.THREAD_GROUP_BG_NONINTERACTIVE - : Process.THREAD_GROUP_DEFAULT; + app.curSchedGroup = schedGroup; return adj; } @@ -13486,9 +14139,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return true; } - int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP); + int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false); - if (app.pid != 0 && app.pid != MY_PID) { + if ((app.pid != 0 && app.pid != MY_PID) || Process.supportsProcesses()) { if (app.curRawAdj != app.setRawAdj) { if (app.curRawAdj > FOREGROUND_APP_ADJ && app.setRawAdj <= FOREGROUND_APP_ADJ) { @@ -13505,7 +14158,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (adj != app.setAdj) { if (Process.setOomAdj(app.pid, adj)) { - if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Log.v( + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v( TAG, "Set app " + app.processName + " oom adj to " + adj); app.setAdj = adj; @@ -13515,7 +14168,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (app.setSchedGroup != app.curSchedGroup) { app.setSchedGroup = app.curSchedGroup; - if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Log.v(TAG, + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, "Setting process group of " + app.processName + " to " + app.curSchedGroup); if (true) { @@ -13523,7 +14176,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { Process.setProcessGroup(app.pid, app.curSchedGroup); } catch (Exception e) { - Log.w(TAG, "Failed setting process group of " + app.pid + Slog.w(TAG, "Failed setting process group of " + app.pid + " to " + app.curSchedGroup); e.printStackTrace(); } finally { @@ -13585,30 +14238,60 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (false) { RuntimeException e = new RuntimeException(); e.fillInStackTrace(); - Log.i(TAG, "updateOomAdj: top=" + TOP_ACT, e); + Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e); } mAdjSeq++; + // Let's determine how many processes we have running vs. + // how many slots we have for background processes; we may want + // to put multiple processes in a slot of there are enough of + // them. + int numSlots = HIDDEN_APP_MAX_ADJ - HIDDEN_APP_MIN_ADJ + 1; + int factor = (mLruProcesses.size()-4)/numSlots; + if (factor < 1) factor = 1; + int step = 0; + int numHidden = 0; + // First try updating the OOM adjustment for each of the // application processes based on their current state. - int i = mLRUProcesses.size(); + int i = mLruProcesses.size(); int curHiddenAdj = HIDDEN_APP_MIN_ADJ; while (i > 0) { i--; - ProcessRecord app = mLRUProcesses.get(i); + ProcessRecord app = mLruProcesses.get(i); + //Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj); if (updateOomAdjLocked(app, curHiddenAdj, TOP_APP)) { - if (curHiddenAdj < HIDDEN_APP_MAX_ADJ + if (curHiddenAdj < EMPTY_APP_ADJ && app.curAdj == curHiddenAdj) { - curHiddenAdj++; + step++; + if (step >= factor) { + step = 0; + curHiddenAdj++; + } + } + if (app.curAdj >= HIDDEN_APP_MIN_ADJ) { + if (!app.killedBackground) { + numHidden++; + if (numHidden > MAX_HIDDEN_APPS) { + Slog.i(TAG, "Kill " + app.processName + + " (pid " + app.pid + "): hidden #" + numHidden + + " beyond limit " + MAX_HIDDEN_APPS); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "too many background"); + app.killedBackground = true; + Process.killProcess(app.pid); + } + } } } else { didOomAdj = false; } } - // todo: for now pretend like OOM ADJ didn't work, because things - // aren't behaving as expected on Linux -- it's not killing processes. + // If we return false, we will fall back on killing processes to + // have a fixed limit. Do this if a limit has been requested; else + // only return false if one of the adjustments failed. return ENFORCE_PROCESS_LIMIT || mProcessLimit > 0 ? false : didOomAdj; } @@ -13622,7 +14305,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final ProcessRecord app = mRemovedProcesses.get(i); if (app.activities.size() == 0 && app.curReceiver == null && app.services.size() == 0) { - Log.i( + Slog.i( TAG, "Exiting empty application process " + app.processName + " (" + (app.thread != null ? app.thread.asBinder() : null) @@ -13655,15 +14338,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Count how many processes are running services. int numServiceProcs = 0; - for (i=mLRUProcesses.size()-1; i>=0; i--) { - final ProcessRecord app = mLRUProcesses.get(i); + for (i=mLruProcesses.size()-1; i>=0; i--) { + final ProcessRecord app = mLruProcesses.get(i); if (app.persistent || app.services.size() != 0 || app.curReceiver != null || app.persistentActivities > 0) { // Don't count processes holding services against our // maximum process count. - if (localLOGV) Log.v( + if (localLOGV) Slog.v( TAG, "Not trimming app " + app + " with services: " + app.services); numServiceProcs++; @@ -13681,15 +14364,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // process count. First remove any processes that no longer // have activites running in them. for ( i=0; - i<mLRUProcesses.size() - && mLRUProcesses.size() > curMaxProcs; + i<mLruProcesses.size() + && mLruProcesses.size() > curMaxProcs; i++) { - final ProcessRecord app = mLRUProcesses.get(i); + final ProcessRecord app = mLruProcesses.get(i); // Quit an application only if it is not currently // running any activities. if (!app.persistent && app.activities.size() == 0 && app.curReceiver == null && app.services.size() == 0) { - Log.i( + Slog.i( TAG, "Exiting empty application process " + app.processName + " (" + (app.thread != null ? app.thread.asBinder() : null) @@ -13714,14 +14397,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If we still have too many processes, now from the least // recently used process we start finishing activities. - if (Config.LOGV) Log.v( - TAG, "*** NOW HAVE " + mLRUProcesses.size() + + if (Config.LOGV) Slog.v( + TAG, "*** NOW HAVE " + mLruProcesses.size() + " of " + curMaxProcs + " processes"); for ( i=0; - i<mLRUProcesses.size() - && mLRUProcesses.size() > curMaxProcs; + i<mLruProcesses.size() + && mLruProcesses.size() > curMaxProcs; i++) { - final ProcessRecord app = mLRUProcesses.get(i); + final ProcessRecord app = mLruProcesses.get(i); // Quit the application only if we have a state saved for // all of its activities. boolean canQuit = !app.persistent && app.curReceiver == null @@ -13729,11 +14412,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen && app.persistentActivities == 0; int NUMA = app.activities.size(); int j; - if (Config.LOGV) Log.v( + if (Config.LOGV) Slog.v( TAG, "Looking to quit " + app.processName); for (j=0; j<NUMA && canQuit; j++) { HistoryRecord r = (HistoryRecord)app.activities.get(j); - if (Config.LOGV) Log.v( + if (Config.LOGV) Slog.v( TAG, " " + r.intent.getComponent().flattenToShortString() + ": frozen=" + r.haveState + ", visible=" + r.visible); canQuit = (r.haveState || !r.stateNotNeeded) @@ -13748,7 +14431,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } r.resultTo = null; } - Log.i(TAG, "Exiting application process " + Slog.i(TAG, "Exiting application process " + app.processName + " (" + (app.thread != null ? app.thread.asBinder() : null) + ")\n"); @@ -13820,8 +14503,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES); } - for (int i = mLRUProcesses.size() - 1 ; i >= 0 ; i--) { - ProcessRecord r = mLRUProcesses.get(i); + for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { + ProcessRecord r = mLruProcesses.get(i); if (r.thread != null && r.persistent) { Process.sendSignal(r.pid, sig); } diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java index 33894d6..3a1aad6 100644 --- a/services/java/com/android/server/am/AppErrorDialog.java +++ b/services/java/com/android/server/am/AppErrorDialog.java @@ -23,7 +23,7 @@ import android.content.DialogInterface; import android.content.res.Resources; import android.os.Handler; import android.os.Message; -import android.util.Log; +import android.util.Slog; class AppErrorDialog extends BaseErrorDialog { private final static String TAG = "AppErrorDialog"; @@ -33,15 +33,12 @@ class AppErrorDialog extends BaseErrorDialog { // Event 'what' codes static final int FORCE_QUIT = 0; - static final int DEBUG = 1; - static final int FORCE_QUIT_AND_REPORT = 2; + static final int FORCE_QUIT_AND_REPORT = 1; // 5-minute timeout, then we automatically dismiss the crash dialog static final long DISMISS_TIMEOUT = 1000 * 60 * 5; - public AppErrorDialog(Context context, AppErrorResult result, - ProcessRecord app, int flags, - String shortMsg, String longMsg) { + public AppErrorDialog(Context context, AppErrorResult result, ProcessRecord app) { super(context); Resources res = context.getResources(); @@ -67,12 +64,6 @@ class AppErrorDialog extends BaseErrorDialog { res.getText(com.android.internal.R.string.force_close), mHandler.obtainMessage(FORCE_QUIT)); - if ((flags&1) != 0) { - setButton(DialogInterface.BUTTON_NEUTRAL, - res.getText(com.android.internal.R.string.debug), - mHandler.obtainMessage(DEBUG)); - } - if (app.errorReportReceiver != null) { setButton(DialogInterface.BUTTON_NEGATIVE, res.getText(com.android.internal.R.string.report), @@ -88,7 +79,7 @@ class AppErrorDialog extends BaseErrorDialog { mHandler.obtainMessage(FORCE_QUIT), DISMISS_TIMEOUT); } - + public void onStop() { } diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java index 03c2a04..9702f91 100644 --- a/services/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/java/com/android/server/am/AppNotRespondingDialog.java @@ -26,7 +26,7 @@ import android.content.res.Resources; import android.os.Handler; import android.os.Message; import android.os.Process; -import android.util.Log; +import android.util.Slog; class AppNotRespondingDialog extends BaseErrorDialog { private static final String TAG = "AppNotRespondingDialog"; @@ -104,8 +104,7 @@ class AppNotRespondingDialog extends BaseErrorDialog { switch (msg.what) { case FORCE_CLOSE: // Kill the application. - mService.killAppAtUsersRequest(mProc, - AppNotRespondingDialog.this, true); + mService.killAppAtUsersRequest(mProc, AppNotRespondingDialog.this); break; case WAIT_AND_REPORT: case WAIT: @@ -114,7 +113,8 @@ class AppNotRespondingDialog extends BaseErrorDialog { ProcessRecord app = mProc; if (msg.what == WAIT_AND_REPORT) { - appErrorIntent = mService.createAppErrorIntentLocked(app); + appErrorIntent = mService.createAppErrorIntentLocked(app, + System.currentTimeMillis(), null); } app.notResponding = false; @@ -130,7 +130,7 @@ class AppNotRespondingDialog extends BaseErrorDialog { try { getContext().startActivity(appErrorIntent); } catch (ActivityNotFoundException e) { - Log.w(TAG, "bug report receiver dissappeared", e); + Slog.w(TAG, "bug report receiver dissappeared", e); } } } diff --git a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java index 0992d4d..8e9818d 100644 --- a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java +++ b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java @@ -62,8 +62,7 @@ class AppWaitingForDebuggerDialog extends BaseErrorDialog { switch (msg.what) { case 1: // Kill the application. - mService.killAppAtUsersRequest(mProc, - AppWaitingForDebuggerDialog.this, true); + mService.killAppAtUsersRequest(mProc, AppWaitingForDebuggerDialog.this); break; } } diff --git a/services/java/com/android/server/am/BackupRecord.java b/services/java/com/android/server/am/BackupRecord.java index 5ac8e0d..6590b91 100644 --- a/services/java/com/android/server/am/BackupRecord.java +++ b/services/java/com/android/server/am/BackupRecord.java @@ -54,4 +54,4 @@ class BackupRecord { .append(' ').append(appInfo.backupAgentName).append('}'); return stringName = sb.toString(); } -}
\ No newline at end of file +} diff --git a/services/java/com/android/server/am/BaseErrorDialog.java b/services/java/com/android/server/am/BaseErrorDialog.java index bed2768..03e3272 100644 --- a/services/java/com/android/server/am/BaseErrorDialog.java +++ b/services/java/com/android/server/am/BaseErrorDialog.java @@ -45,10 +45,10 @@ class BaseErrorDialog extends AlertDialog { public boolean dispatchKeyEvent(KeyEvent event) { if (mConsuming) { - //Log.i(TAG, "Consuming: " + event); + //Slog.i(TAG, "Consuming: " + event); return true; } - //Log.i(TAG, "Dispatching: " + event); + //Slog.i(TAG, "Dispatching: " + event); return super.dispatchKeyEvent(event); } @@ -61,6 +61,10 @@ class BaseErrorDialog extends AlertDialog { if (b != null) { b.setEnabled(enabled); } + b = (Button)findViewById(R.id.button3); + if (b != null) { + b.setEnabled(enabled); + } } private Handler mHandler = new Handler() { diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index d59aead..33bbc13 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -24,7 +24,7 @@ import android.os.Parcel; import android.os.Process; import android.os.ServiceManager; import android.telephony.SignalStrength; -import android.util.Log; +import android.util.Slog; import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsImpl; @@ -57,7 +57,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } public void shutdown() { - Log.w("BatteryStats", "Writing battery stats before shutdown..."); + Slog.w("BatteryStats", "Writing battery stats before shutdown..."); synchronized (mStats) { mStats.writeLocked(); } @@ -84,8 +84,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub { public byte[] getStatistics() { mContext.enforceCallingPermission( android.Manifest.permission.BATTERY_STATS, null); - //Log.i("foo", "SENDING BATTERY INFO:"); - //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo")); + //Slog.i("foo", "SENDING BATTERY INFO:"); + //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM)); Parcel out = Parcel.obtain(); mStats.writeToParcel(out, 0); byte[] data = out.marshall(); @@ -158,9 +158,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub { public void noteInputEvent() { enforceCallingPermission(); - synchronized (mStats) { - mStats.noteInputEventLocked(); - } + mStats.noteInputEventAtomic(); } public void noteUserActivity(int uid, int event) { diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index 75c9600..c3f0b3e 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -126,7 +126,9 @@ class BroadcastRecord extends Binder { pw.println(prefix + "curApp=" + curApp); pw.println(prefix + "curComponent=" + (curComponent != null ? curComponent.toShortString() : "--")); - pw.println(prefix + "curSourceDir=" + curReceiver.applicationInfo.sourceDir); + if (curReceiver != null && curReceiver.applicationInfo != null) { + pw.println(prefix + "curSourceDir=" + curReceiver.applicationInfo.sourceDir); + } } String stateStr = " (?)"; switch (state) { diff --git a/services/java/com/android/server/am/DeviceMonitor.java b/services/java/com/android/server/am/DeviceMonitor.java index ce07430..5f3b0ce 100644 --- a/services/java/com/android/server/am/DeviceMonitor.java +++ b/services/java/com/android/server/am/DeviceMonitor.java @@ -16,7 +16,7 @@ package com.android.server.am; -import android.util.Log; +import android.util.Slog; import java.io.*; import java.util.Arrays; @@ -65,7 +65,7 @@ class DeviceMonitor { try { dump(); } catch (IOException e) { - Log.w(LOG_TAG, "Dump failed.", e); + Slog.w(LOG_TAG, "Dump failed.", e); } pause(); } @@ -100,7 +100,7 @@ class DeviceMonitor { Arrays.sort(files); for (int i = 0; i < count; i++) { if (!files[i].delete()) { - Log.w(LOG_TAG, "Couldn't delete " + files[i] + "."); + Slog.w(LOG_TAG, "Couldn't delete " + files[i] + "."); } } } @@ -178,7 +178,7 @@ class DeviceMonitor { closeable.close(); } } catch (IOException e) { - Log.w(LOG_TAG, e); + Slog.w(LOG_TAG, e); } } diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags new file mode 100644 index 0000000..aadd37d --- /dev/null +++ b/services/java/com/android/server/am/EventLogTags.logtags @@ -0,0 +1,85 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +option java_package com.android.server.am + +2719 configuration_changed (config mask|1|5) +2721 cpu (total|1|6),(user|1|6),(system|1|6),(iowait|1|6),(irq|1|6),(softirq|1|6) + +# ActivityManagerService.systemReady() starts: +3040 boot_progress_ams_ready (time|2|3) +# ActivityManagerService calls enableScreenAfterBoot(): +3050 boot_progress_enable_screen (time|2|3) + +# Do not change these names without updating the checkin_events setting in +# google3/googledata/wireless/android/provisioning/gservices.config !! +# +# An activity is being finished: +30001 am_finish_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3) +# A task is being brought to the front of the screen: +30002 am_task_to_front (Task|1|5) +# An existing activity is being given a new intent: +30003 am_new_intent (Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) +# A new task is being created: +30004 am_create_task (Task ID|1|5) +# A new activity is being created in an existing task: +30005 am_create_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) +# An activity has been resumed into the foreground but was not already running: +30006 am_restart_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +# An activity has been resumed and is now in the foreground: +30007 am_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +# Application Not Responding +30008 am_anr (pid|1|5),(Package Name|3),(Flags|1|5),(reason|3) +# Activity launch time +30009 activity_launch_time (Token|1|5),(Component Name|3),(time|2|3) +# Application process bound to work +30010 am_proc_bound (PID|1|5),(Process Name|3) +# Application process died +30011 am_proc_died (PID|1|5),(Process Name|3) +# The Activity Manager failed to pause the given activity. +30012 am_failed_to_pause (Token|1|5),(Wanting to pause|3),(Currently pausing|3) +# Attempting to pause the current activity +30013 am_pause_activity (Token|1|5),(Component Name|3) +# Application process has been started +30014 am_proc_start (PID|1|5),(UID|1|5),(Process Name|3),(Type|3),(Component|3) +# An application process has been marked as bad +30015 am_proc_bad (UID|1|5),(Process Name|3) +# An application process that was bad is now marked as good +30016 am_proc_good (UID|1|5),(Process Name|3) +# Reporting to applications that memory is low +30017 am_low_memory (Num Processes|1|1) +# An activity is being destroyed: +30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +# An activity has been relaunched, resumed, and is now in the foreground: +30019 am_relaunch_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +# An activity has been relaunched: +30020 am_relaunch_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +# The activity's onPause has been called. +30021 am_on_paused_called (Component Name|3) +# The activity's onResume has been called. +30022 am_on_resume_called (Component Name|3) +# Kill a process to reclaim memory. +30023 am_kill (PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3) +# Discard an undelivered serialized broadcast (timeout/ANR/crash) +30024 am_broadcast_discard_filter (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5) +30025 am_broadcast_discard_app (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3) +# A service is being created +30030 am_create_service (Service Record|1|5),(Name|3),(Intent|3),(PID|1|5) +# A service is being destroyed +30031 am_destroy_service (Service Record|1|5),(Name|3),(PID|1|5) +# A process has crashed too many times, it is being cleared +30032 am_process_crashed_too_much (Name|3),(PID|1|5) +# An unknown process is trying to attach to the activity manager +30033 am_drop_process (PID|1|5) +# A service has crashed too many times, it is being stopped +30034 am_service_crashed_too_much (Crash Count|1|1),(Component Name|3),(PID|1|5) +# A service is going to be restarted after its process went away +30035 am_schedule_service_restart (Component Name|3),(Time|2|3) +# A client was waiting for a content provider, but its process was lost +30036 am_provider_lost_process (Package Name|3),(UID|1|5),(Name|3) +# The activity manager gave up on a new process taking too long to start +30037 am_process_start_timeout (PID|1|5),(UID|1|5),(Process Name|3) + +# Unhandled exception +30039 am_crash (PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5) +# Log.wtf() called +30040 am_wtf (PID|1|5),(Process Name|3),(Flags|1|5),(Tag|3),(Message|3) diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java index 84ded22..dca7a99 100644 --- a/services/java/com/android/server/am/HistoryRecord.java +++ b/services/java/com/android/server/am/HistoryRecord.java @@ -373,7 +373,7 @@ class HistoryRecord extends IApplicationToken.Stub { final long totalTime = service.mInitialStartTime != 0 ? (curTime - service.mInitialStartTime) : thisTime; if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) { - EventLog.writeEvent(ActivityManagerService.LOG_ACTIVITY_LAUNCH_TIME, + EventLog.writeEvent(EventLogTags.ACTIVITY_LAUNCH_TIME, System.identityHashCode(this), shortComponentName, thisTime, totalTime); StringBuilder sb = service.mStringBuilder; @@ -387,12 +387,14 @@ class HistoryRecord extends IApplicationToken.Stub { sb.append(" ms)"); Log.i(ActivityManagerService.TAG, sb.toString()); } + service.reportActivityLaunchedLocked(false, this, thisTime, totalTime); if (totalTime > 0) { service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime); } startTime = 0; service.mInitialStartTime = 0; } + service.reportActivityVisibleLocked(this); if (ActivityManagerService.DEBUG_SWITCH) Log.v( ActivityManagerService.TAG, "windowsVisible(): " + this); if (!nowVisible) { @@ -456,8 +458,10 @@ class HistoryRecord extends IApplicationToken.Stub { } public boolean keyDispatchingTimedOut() { + HistoryRecord r; + ProcessRecord anrApp = null; synchronized(service) { - HistoryRecord r = getWaitingHistoryRecordLocked(); + r = getWaitingHistoryRecordLocked(); if (r != null && r.app != null) { if (r.app.debugging) { return false; @@ -470,8 +474,7 @@ class HistoryRecord extends IApplicationToken.Stub { } if (r.app.instrumentationClass == null) { - service.appNotRespondingLocked(r.app, r, this, - "keyDispatchingTimedOut"); + anrApp = r.app; } else { Bundle info = new Bundle(); info.putString("shortMsg", "keyDispatchingTimedOut"); @@ -480,8 +483,14 @@ class HistoryRecord extends IApplicationToken.Stub { r.app, Activity.RESULT_CANCELED, info); } } - return true; } + + if (anrApp != null) { + service.appNotResponding(anrApp, r, this, + "keyDispatchingTimedOut"); + } + + return true; } /** Returns the key dispatching timeout for this application token. */ diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index b3086d5..847e91b 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -24,7 +24,7 @@ import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.util.Log; +import android.util.Slog; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -81,7 +81,7 @@ class PendingIntentRecord extends IIntentSender.Stub { hash = (ODD_PRIME_NUMBER*hash) + _p.hashCode(); hash = (ODD_PRIME_NUMBER*hash) + _t; hashCode = hash; - //Log.i(ActivityManagerService.TAG, this + " hashCode=0x" + //Slog.i(ActivityManagerService.TAG, this + " hashCode=0x" // + Integer.toHexString(hashCode)); } @@ -145,8 +145,9 @@ class PendingIntentRecord extends IIntentSender.Stub { public String toString() { return "Key{" + typeName() + " pkg=" + packageName - + " intent=" + requestIntent.toShortString(true, false) + " flags=0x" - + Integer.toHexString(flags) + "}"; + + " intent=" + + (requestIntent != null ? requestIntent.toShortString(true, false) : "<null>") + + " flags=0x" + Integer.toHexString(flags) + "}"; } String typeName() { @@ -212,7 +213,7 @@ class PendingIntentRecord extends IIntentSender.Stub { finalIntent, resolvedType, resultTo, resultWho, requestCode, false); } catch (RuntimeException e) { - Log.w(ActivityManagerService.TAG, + Slog.w(ActivityManagerService.TAG, "Unable to send startActivity intent", e); } break; @@ -230,7 +231,7 @@ class PendingIntentRecord extends IIntentSender.Stub { (finishedReceiver != null), false); sendFinish = false; } catch (RuntimeException e) { - Log.w(ActivityManagerService.TAG, + Slog.w(ActivityManagerService.TAG, "Unable to send startActivity intent", e); } break; @@ -239,7 +240,7 @@ class PendingIntentRecord extends IIntentSender.Stub { owner.startServiceInPackage(uid, finalIntent, resolvedType); } catch (RuntimeException e) { - Log.w(ActivityManagerService.TAG, + Slog.w(ActivityManagerService.TAG, "Unable to send startService intent", e); } break; @@ -262,17 +263,26 @@ class PendingIntentRecord extends IIntentSender.Stub { } protected void finalize() throws Throwable { - if (!canceled) { - synchronized(owner) { - WeakReference<PendingIntentRecord> current = - owner.mIntentSenderRecords.get(key); - if (current == ref) { - owner.mIntentSenderRecords.remove(key); - } + try { + if (!canceled) { + owner.mHandler.sendMessage(owner.mHandler.obtainMessage( + ActivityManagerService.FINALIZE_PENDING_INTENT_MSG, this)); } + } finally { + super.finalize(); } } + public void completeFinalize() { + synchronized(owner) { + WeakReference<PendingIntentRecord> current = + owner.mIntentSenderRecords.get(key); + if (current == ref) { + owner.mIntentSenderRecords.remove(key); + } + } + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("uid="); pw.print(uid); pw.print(" packageName="); pw.print(key.packageName); @@ -286,8 +296,10 @@ class PendingIntentRecord extends IIntentSender.Stub { pw.print(prefix); pw.print("requestCode="); pw.print(key.requestCode); pw.print(" requestResolvedType="); pw.println(key.requestResolvedType); } - pw.print(prefix); pw.print("requestIntent="); - pw.println(key.requestIntent.toShortString(true, true)); + if (key.requestIntent != null) { + pw.print(prefix); pw.print("requestIntent="); + pw.println(key.requestIntent.toShortString(true, true)); + } if (sent || canceled) { pw.print(prefix); pw.print("sent="); pw.print(sent); pw.print(" canceled="); pw.println(canceled); diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 6202257..7620468 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -28,6 +28,7 @@ import android.content.pm.ApplicationInfo; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.util.PrintWriterPrinter; import java.io.PrintWriter; @@ -50,6 +51,8 @@ class ProcessRecord implements Watchdog.PssRequestor { // are in the process of launching the app) int pid; // The process of this application; 0 if none boolean starting; // True if the process is being started + long lastActivityTime; // For managing the LRU list + long lruWeight; // Weight for ordering in LRU list int maxAdj; // Maximum OOM adjustment for this process int hiddenAdj; // If hidden, this is the adjustment to use int curRawAdj; // Current OOM unlimited adjustment for this process @@ -61,6 +64,7 @@ class ProcessRecord implements Watchdog.PssRequestor { boolean setIsForeground; // Running foreground UI when last set? boolean foregroundServices; // Running any services that are foreground? boolean bad; // True if disabled in the bad process list + boolean killedBackground; // True when proc has been killed due to too many bg IBinder forcingToForeground;// Token that is forcing this process to be foreground int adjSeq; // Sequence id for identifying repeated trav ComponentName instrumentationClass;// class installed to instrument app @@ -73,6 +77,8 @@ class ProcessRecord implements Watchdog.PssRequestor { long lastRequestedGc; // When we last asked the app to do a gc long lastLowMemory; // When we last told the app that memory is low boolean reportLowMemory; // Set to true when waiting to report low mem + boolean empty; // Is this an empty background process? + boolean hidden; // Is this a hidden process? int lastPss; // Last pss size reported by app. String adjType; // Debugging: primary thing impacting oom_adj. int adjTypeCode; // Debugging: adj code to report to app. @@ -108,6 +114,7 @@ class ProcessRecord implements Watchdog.PssRequestor { boolean waitedForDebugger; // has process show wait for debugger dialog? Dialog waitDialog; // current wait for debugger dialog + String shortStringName; // caching of toShortString() result. String stringName; // caching of toString() result. // These reports are generated & stored when an app gets into an error condition. @@ -120,6 +127,7 @@ class ProcessRecord implements Watchdog.PssRequestor { ComponentName errorReportReceiver; void dump(PrintWriter pw, String prefix) { + long now = SystemClock.uptimeMillis(); if (info.className != null) { pw.print(prefix); pw.print("class="); pw.println(info.className); } @@ -149,6 +157,10 @@ class ProcessRecord implements Watchdog.PssRequestor { pw.print(" curReceiver="); pw.println(curReceiver); pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting="); pw.print(starting); pw.print(" lastPss="); pw.println(lastPss); + pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime); + pw.print(" lruWeight="); pw.println(lruWeight); + pw.print(" hidden="); pw.print(hidden); + pw.print(" empty="); pw.println(empty); pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj); pw.print(" hidden="); pw.print(hiddenAdj); pw.print(" curRaw="); pw.print(curRawAdj); @@ -163,6 +175,9 @@ class ProcessRecord implements Watchdog.PssRequestor { pw.print(prefix); pw.print("persistent="); pw.print(persistent); pw.print(" removed="); pw.print(removed); pw.print(" persistentActivities="); pw.println(persistentActivities); + if (killedBackground) { + pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground); + } if (debugging || crashing || crashDialog != null || notResponding || anrDialog != null || bad) { pw.print(prefix); pw.print("debugging="); pw.print(debugging); @@ -220,6 +235,7 @@ class ProcessRecord implements Watchdog.PssRequestor { public void setPid(int _pid) { pid = _pid; + shortStringName = null; stringName = null; } @@ -256,12 +272,16 @@ class ProcessRecord implements Watchdog.PssRequestor { } } - public String toString() { - if (stringName != null) { - return stringName; + public String toShortString() { + if (shortStringName != null) { + return shortStringName; } StringBuilder sb = new StringBuilder(128); - sb.append("ProcessRecord{"); + toShortString(sb); + return shortStringName = sb.toString(); + } + + void toShortString(StringBuilder sb) { sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); sb.append(pid); @@ -269,6 +289,15 @@ class ProcessRecord implements Watchdog.PssRequestor { sb.append(processName); sb.append('/'); sb.append(info.uid); + } + + public String toString() { + if (stringName != null) { + return stringName; + } + StringBuilder sb = new StringBuilder(128); + sb.append("ProcessRecord{"); + toShortString(sb); sb.append('}'); return stringName = sb.toString(); } diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 2f2cc32..0542497 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -29,7 +29,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; -import android.util.Log; +import android.util.Slog; import java.io.PrintWriter; import java.util.ArrayList; @@ -269,8 +269,12 @@ class ServiceRecord extends Binder { inm.enqueueNotification(localPackageName, localForegroundId, localForegroundNoti, outId); } catch (RuntimeException e) { - Log.w(ActivityManagerService.TAG, "Error showing notification for service", - e); + Slog.w(ActivityManagerService.TAG, + "Error showing notification for service", e); + // If it gave us a garbage notification, it doesn't + // get to be foreground. + ams.setServiceForeground(name, ServiceRecord.this, + localForegroundId, null, true); } catch (RemoteException e) { } } @@ -293,8 +297,8 @@ class ServiceRecord extends Binder { try { inm.cancelNotification(localPackageName, localForegroundId); } catch (RuntimeException e) { - Log.w(ActivityManagerService.TAG, "Error canceling notification for" - + " service", e); + Slog.w(ActivityManagerService.TAG, + "Error canceling notification for service", e); } catch (RemoteException e) { } } diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index f99ca96..1b9e1c7 100644 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -27,7 +27,7 @@ import android.os.Parcel; import android.os.Process; import android.os.ServiceManager; import android.os.SystemClock; -import android.util.Log; +import android.util.Slog; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -149,14 +149,14 @@ public final class UsageStatsService extends IUsageStats.Stub { PkgUsageStatsExtended(Parcel in) { mLaunchCount = in.readInt(); mUsageTime = in.readLong(); - if (localLOGV) Log.v(TAG, "Launch count: " + mLaunchCount + if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount + ", Usage time:" + mUsageTime); final int N = in.readInt(); - if (localLOGV) Log.v(TAG, "Reading comps: " + N); + if (localLOGV) Slog.v(TAG, "Reading comps: " + N); for (int i=0; i<N; i++) { String comp = in.readString(); - if (localLOGV) Log.v(TAG, "Component: " + comp); + if (localLOGV) Slog.v(TAG, "Component: " + comp); TimeStats times = new TimeStats(in); mLaunchTimes.put(comp, times); } @@ -231,7 +231,7 @@ public final class UsageStatsService extends IUsageStats.Stub { while (i > 0) { i--; if (fList[i].startsWith(prefix)) { - Log.i(TAG, "Deleting old usage file: " + fList[i]); + Slog.i(TAG, "Deleting old usage file: " + fList[i]); (new File(parentDir, fList[i])).delete(); } } @@ -291,7 +291,7 @@ public final class UsageStatsService extends IUsageStats.Stub { newFile.createNewFile(); } } catch (IOException e) { - Log.w(TAG,"Error : " + e + " reading data from file:" + newFile); + Slog.w(TAG,"Error : " + e + " reading data from file:" + newFile); } } } @@ -300,7 +300,7 @@ public final class UsageStatsService extends IUsageStats.Stub { Parcel in = getParcelForFile(file); int vers = in.readInt(); if (vers != VERSION) { - Log.w(TAG, "Usage stats version changed; dropping"); + Slog.w(TAG, "Usage stats version changed; dropping"); return; } int N = in.readInt(); @@ -310,7 +310,7 @@ public final class UsageStatsService extends IUsageStats.Stub { if (pkgName == null) { break; } - if (localLOGV) Log.v(TAG, "Reading package #" + N + ": " + pkgName); + if (localLOGV) Slog.v(TAG, "Reading package #" + N + ": " + pkgName); PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in); synchronized (mStatsLock) { mStats.put(pkgName, pus); @@ -356,7 +356,7 @@ public final class UsageStatsService extends IUsageStats.Stub { for (int i = 0; i < count; i++) { String fileName = fileList.get(i); File file = new File(mDir, fileName); - Log.i(TAG, "Deleting usage file : " + fileName); + Slog.i(TAG, "Deleting usage file : " + fileName); file.delete(); } } @@ -383,9 +383,13 @@ public final class UsageStatsService extends IUsageStats.Stub { File backupFile = null; if (mFile != null && mFile.exists()) { backupFile = new File(mFile.getPath() + ".bak"); - if (!mFile.renameTo(backupFile)) { - Log.w(TAG, "Failed to persist new stats"); - return; + if (!backupFile.exists()) { + if (!mFile.renameTo(backupFile)) { + Slog.w(TAG, "Failed to persist new stats"); + return; + } + } else { + mFile.delete(); } } @@ -407,7 +411,7 @@ public final class UsageStatsService extends IUsageStats.Stub { backupFile.delete(); } } catch (IOException e) { - Log.w(TAG, "Failed writing stats to file:" + mFile); + Slog.w(TAG, "Failed writing stats to file:" + mFile); if (backupFile != null) { mFile.delete(); backupFile.renameTo(mFile); @@ -448,7 +452,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } public void shutdown() { - Log.w(TAG, "Writing usage stats before shutdown..."); + Slog.w(TAG, "Writing usage stats before shutdown..."); writeStatsToFile(true); } @@ -475,7 +479,7 @@ public final class UsageStatsService extends IUsageStats.Stub { if (mLastResumedPkg != null) { // We last resumed some other package... just pause it now // to recover. - Log.i(TAG, "Unexpected resume of " + pkgName + Slog.i(TAG, "Unexpected resume of " + pkgName + " while already resumed in " + mLastResumedPkg); PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg); if (pus != null) { @@ -491,7 +495,7 @@ public final class UsageStatsService extends IUsageStats.Stub { mLastResumedPkg = pkgName; mLastResumedComp = componentName.getClassName(); - if (localLOGV) Log.i(TAG, "started component:" + pkgName); + if (localLOGV) Slog.i(TAG, "started component:" + pkgName); PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { pus = new PkgUsageStatsExtended(); @@ -514,18 +518,18 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } if (!mIsResumed) { - Log.i(TAG, "Something wrong here, didn't expect " + Slog.i(TAG, "Something wrong here, didn't expect " + pkgName + " to be paused"); return; } mIsResumed = false; - if (localLOGV) Log.i(TAG, "paused component:"+pkgName); + if (localLOGV) Slog.i(TAG, "paused component:"+pkgName); PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { // Weird some error here - Log.i(TAG, "No package stats for pkg:"+pkgName); + Slog.i(TAG, "No package stats for pkg:"+pkgName); return; } pus.updatePause(); @@ -642,10 +646,10 @@ public final class UsageStatsService extends IUsageStats.Stub { dFile.delete(); } } catch (FileNotFoundException e) { - Log.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file); + Slog.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); + Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file); } } } @@ -829,7 +833,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } else if (isCheckinRequest) { // If checkin doesn't specify any packages, then we simply won't // show anything. - Log.w(TAG, "Checkin without packages"); + Slog.w(TAG, "Checkin without packages"); return; } diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java new file mode 100644 index 0000000..b43b86c --- /dev/null +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -0,0 +1,1453 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +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.ConnectivityManager; +import android.net.InterfaceConfiguration; +import android.net.IConnectivityManager; +import android.net.INetworkManagementEventObserver; +import android.net.NetworkInfo; +import android.net.NetworkUtils; +import android.os.BatteryManager; +import android.os.Binder; +import android.os.Environment; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.util.Log; + +import com.android.internal.telephony.Phone; +import com.android.internal.util.HierarchicalState; +import com.android.internal.util.HierarchicalStateMachine; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; +/** + * @hide + * + * Timeout + * + * TODO - look for parent classes and code sharing + */ + +public class Tethering extends INetworkManagementEventObserver.Stub { + + private Context mContext; + private final String TAG = "Tethering"; + + private boolean mBooted = false; + //used to remember if we got connected before boot finished + private boolean mDeferedUsbConnection = false; + + // TODO - remove both of these - should be part of interface inspection/selection stuff + private String[] mTetherableUsbRegexs; + private String[] mTetherableWifiRegexs; + private String[] mUpstreamIfaceRegexs; + + private Looper mLooper; + private HandlerThread mThread; + + private HashMap<String, TetherInterfaceSM> mIfaces; // all tethered/tetherable ifaces + + private BroadcastReceiver mStateReceiver; + + private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129"; + private static final String USB_NETMASK = "255.255.255.0"; + + // FYI - the default wifi is 192.168.43.1 and 255.255.255.0 + + private String[] mDhcpRange; + private static final String DHCP_DEFAULT_RANGE1_START = "192.168.42.2"; + private static final String DHCP_DEFAULT_RANGE1_STOP = "192.168.42.254"; + private static final String DHCP_DEFAULT_RANGE2_START = "192.168.43.2"; + private static final String DHCP_DEFAULT_RANGE2_STOP = "192.168.43.254"; + + private String[] mDnsServers; + private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8"; + private static final String DNS_DEFAULT_SERVER2 = "4.2.2.2"; + + private boolean mDunRequired; // configuration info - must use DUN apn on 3g + + private HierarchicalStateMachine mTetherMasterSM; + + private Notification mTetheredNotification; + + // whether we can tether is the && of these two - they come in as separate + // broadcasts so track them so we can decide what to do when either changes + private boolean mUsbMassStorageOff; // track the status of USB Mass Storage + private boolean mUsbConnected; // track the status of USB connection + + public Tethering(Context context, Looper looper) { + Log.d(TAG, "Tethering starting"); + mContext = context; + mLooper = looper; + + // register for notifications from NetworkManagement Service + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + try { + service.registerObserver(this); + } catch (RemoteException e) { + Log.e(TAG, "Error registering observer :" + e); + } + + mIfaces = new HashMap<String, TetherInterfaceSM>(); + + // make our own thread so we don't anr the system + mThread = new HandlerThread("Tethering"); + mThread.start(); + mLooper = mThread.getLooper(); + mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper); + mTetherMasterSM.start(); + + mStateReceiver = new StateReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.addAction(Intent.ACTION_BOOT_COMPLETED); + mContext.registerReceiver(mStateReceiver, filter); + + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_MEDIA_SHARED); + filter.addAction(Intent.ACTION_MEDIA_UNSHARED); + filter.addDataScheme("file"); + mContext.registerReceiver(mStateReceiver, filter); + + mUsbMassStorageOff = !Environment.MEDIA_SHARED.equals( + Environment.getExternalStorageState()); + + mDhcpRange = context.getResources().getStringArray( + com.android.internal.R.array.config_tether_dhcp_range); + if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) { + mDhcpRange = new String[4]; + mDhcpRange[0] = DHCP_DEFAULT_RANGE1_START; + mDhcpRange[1] = DHCP_DEFAULT_RANGE1_STOP; + mDhcpRange[2] = DHCP_DEFAULT_RANGE2_START; + mDhcpRange[3] = DHCP_DEFAULT_RANGE2_STOP; + } + mDunRequired = context.getResources().getBoolean( + com.android.internal.R.bool.config_tether_dun_required); + + mTetherableUsbRegexs = context.getResources().getStringArray( + com.android.internal.R.array.config_tether_usb_regexs); + mTetherableWifiRegexs = context.getResources().getStringArray( + com.android.internal.R.array.config_tether_wifi_regexs); + mUpstreamIfaceRegexs = context.getResources().getStringArray( + com.android.internal.R.array.config_tether_upstream_regexs); + + // TODO - remove and rely on real notifications of the current iface + mDnsServers = new String[2]; + mDnsServers[0] = DNS_DEFAULT_SERVER1; + mDnsServers[1] = DNS_DEFAULT_SERVER2; + } + + public void interfaceLinkStatusChanged(String iface, boolean link) { + Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link); + boolean found = false; + boolean usb = false; + if (isWifi(iface)) { + found = true; + } else if (isUsb(iface)) { + found = true; + usb = true; + } + if (found == false) return; + + synchronized (mIfaces) { + TetherInterfaceSM sm = mIfaces.get(iface); + if (link) { + if (sm == null) { + sm = new TetherInterfaceSM(iface, mLooper, usb); + mIfaces.put(iface, sm); + sm.start(); + } + } else { + if (sm != null) { + sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN); + mIfaces.remove(iface); + } + } + } + } + + private boolean isUsb(String iface) { + for (String regex : mTetherableUsbRegexs) { + if (iface.matches(regex)) return true; + } + return false; + } + + public boolean isWifi(String iface) { + for (String regex : mTetherableWifiRegexs) { + if (iface.matches(regex)) return true; + } + return false; + } + + public void interfaceAdded(String iface) { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + boolean found = false; + boolean usb = false; + if (isWifi(iface)) { + found = true; + } + if (isUsb(iface)) { + found = true; + usb = true; + } + if (found == false) { + Log.d(TAG, iface + " is not a tetherable iface, ignoring"); + return; + } + + synchronized (mIfaces) { + TetherInterfaceSM sm = mIfaces.get(iface); + if (sm != null) { + Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring"); + return; + } + sm = new TetherInterfaceSM(iface, mLooper, usb); + mIfaces.put(iface, sm); + sm.start(); + } + Log.d(TAG, "interfaceAdded :" + iface); + } + + public void interfaceRemoved(String iface) { + synchronized (mIfaces) { + TetherInterfaceSM sm = mIfaces.get(iface); + if (sm == null) { + Log.e(TAG, "attempting to remove unknown iface (" + iface + "), ignoring"); + return; + } + sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN); + mIfaces.remove(iface); + } + } + + public int tether(String iface) { + Log.d(TAG, "Tethering " + iface); + TetherInterfaceSM sm = null; + synchronized (mIfaces) { + sm = mIfaces.get(iface); + } + if (sm == null) { + Log.e(TAG, "Tried to Tether an unknown iface :" + iface + ", ignoring"); + return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; + } + if (!sm.isAvailable() && !sm.isErrored()) { + Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring"); + return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE; + } + sm.sendMessage(TetherInterfaceSM.CMD_TETHER_REQUESTED); + return ConnectivityManager.TETHER_ERROR_NO_ERROR; + } + + public int untether(String iface) { + Log.d(TAG, "Untethering " + iface); + TetherInterfaceSM sm = null; + synchronized (mIfaces) { + sm = mIfaces.get(iface); + } + if (sm == null) { + Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring"); + return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; + } + if (sm.isErrored()) { + Log.e(TAG, "Tried to Untethered an errored iface :" + iface + ", ignoring"); + return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE; + } + sm.sendMessage(TetherInterfaceSM.CMD_TETHER_UNREQUESTED); + return ConnectivityManager.TETHER_ERROR_NO_ERROR; + } + + public int getLastTetherError(String iface) { + TetherInterfaceSM sm = null; + synchronized (mIfaces) { + sm = mIfaces.get(iface); + } + if (sm == null) { + Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface + ", ignoring"); + return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; + } + return sm.getLastError(); + } + + private void sendTetherStateChangedBroadcast() { + IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); + try { + if (!service.isTetheringSupported()) return; + } catch (RemoteException e) { + return; + } + + ArrayList<String> availableList = new ArrayList<String>(); + ArrayList<String> activeList = new ArrayList<String>(); + ArrayList<String> erroredList = new ArrayList<String>(); + + boolean wifiTethered = false; + boolean usbTethered = false; + + synchronized (mIfaces) { + Set ifaces = mIfaces.keySet(); + for (Object iface : ifaces) { + TetherInterfaceSM sm = mIfaces.get(iface); + if (sm != null) { + if(sm.isErrored()) { + erroredList.add((String)iface); + } else if (sm.isAvailable()) { + availableList.add((String)iface); + } else if (sm.isTethered()) { + if (isUsb((String)iface)) { + usbTethered = true; + } else if (isWifi((String)iface)) { + wifiTethered = true; + } + activeList.add((String)iface); + } + } + } + } + Intent broadcast = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); + broadcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_AVAILABLE_TETHER, + availableList); + broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, activeList); + broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER, + erroredList); + mContext.sendStickyBroadcast(broadcast); + Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " + + activeList.size() + ", " + erroredList.size()); + + if (usbTethered) { + if (wifiTethered) { + showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_general); + } else { + showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_usb); + } + } else if (wifiTethered) { + showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_wifi); + } else { + clearTetheredNotification(); + } + } + + private void showTetheredNotification(int icon) { + NotificationManager notificationManager = + (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager == null) { + return; + } + + if (mTetheredNotification != null) { + if (mTetheredNotification.icon == icon) { + return; + } + notificationManager.cancel(mTetheredNotification.icon); + } + + Intent intent = new Intent(); + intent.setClassName("com.android.settings", "com.android.settings.TetherSettings"); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + Resources r = Resources.getSystem(); + CharSequence title = r.getText(com.android.internal.R.string.tethered_notification_title); + CharSequence message = r.getText(com.android.internal.R.string. + tethered_notification_message); + + if(mTetheredNotification == null) { + mTetheredNotification = new Notification(); + mTetheredNotification.when = 0; + } + mTetheredNotification.icon = icon; + mTetheredNotification.defaults &= ~Notification.DEFAULT_SOUND; + mTetheredNotification.flags = Notification.FLAG_ONGOING_EVENT; + mTetheredNotification.tickerText = title; + mTetheredNotification.setLatestEventInfo(mContext, title, message, pi); + + notificationManager.notify(mTetheredNotification.icon, mTetheredNotification); + } + + private void clearTetheredNotification() { + NotificationManager notificationManager = + (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null && mTetheredNotification != null) { + notificationManager.cancel(mTetheredNotification.icon); + mTetheredNotification = null; + } + } + + private void updateUsbStatus() { + boolean enable = mUsbConnected && mUsbMassStorageOff; + + if (mBooted) { + enableUsbIfaces(enable); + } + } + + private class StateReceiver extends BroadcastReceiver { + public void onReceive(Context content, Intent intent) { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { + mUsbConnected = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) + == BatteryManager.BATTERY_PLUGGED_USB); + Tethering.this.updateUsbStatus(); + } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) { + mUsbMassStorageOff = false; + updateUsbStatus(); + } + else if (action.equals(Intent.ACTION_MEDIA_UNSHARED)) { + mUsbMassStorageOff = true; + updateUsbStatus(); + } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + Log.d(TAG, "Tethering got CONNECTIVITY_ACTION"); + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED); + } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { + mBooted = true; + updateUsbStatus(); + } + } + } + + // used on cable insert/remove + private void enableUsbIfaces(boolean enable) { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + String[] ifaces = new String[0]; + try { + ifaces = service.listInterfaces(); + } catch (Exception e) { + Log.e(TAG, "Error listing Interfaces :" + e); + return; + } + for (String iface : ifaces) { + if (isUsb(iface)) { + if (enable) { + interfaceAdded(iface); + } else { + interfaceRemoved(iface); + } + } + } + } + + // toggled when we enter/leave the fully teathered state + private boolean enableUsbRndis(boolean enabled) { + Log.d(TAG, "enableUsbRndis(" + enabled + ")"); + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + + try { + if (enabled) { + synchronized (this) { + if (!service.isUsbRNDISStarted()) { + service.startUsbRNDIS(); + } + } + } else { + if (service.isUsbRNDISStarted()) { + service.stopUsbRNDIS(); + } + } + } catch (Exception e) { + Log.e(TAG, "Error toggling usb RNDIS :" + e); + return false; + } + return true; + } + + // configured when we start tethering and unconfig'd on error or conclusion + private boolean configureUsbIface(boolean enabled) { + Log.d(TAG, "configureUsbIface(" + enabled + ")"); + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + + // bring toggle the interfaces + String[] ifaces = new String[0]; + try { + ifaces = service.listInterfaces(); + } catch (Exception e) { + Log.e(TAG, "Error listing Interfaces :" + e); + return false; + } + for (String iface : ifaces) { + if (isUsb(iface)) { + InterfaceConfiguration ifcg = null; + try { + ifcg = service.getInterfaceConfig(iface); + if (ifcg != null) { + String[] addr = USB_NEAR_IFACE_ADDR.split("\\."); + ifcg.ipAddr = (Integer.parseInt(addr[0]) << 24) + + (Integer.parseInt(addr[1]) << 16) + + (Integer.parseInt(addr[2]) << 8) + + (Integer.parseInt(addr[3])); + addr = USB_NETMASK.split("\\."); + ifcg.netmask = (Integer.parseInt(addr[0]) << 24) + + (Integer.parseInt(addr[1]) << 16) + + (Integer.parseInt(addr[2]) << 8) + + (Integer.parseInt(addr[3])); + if (enabled) { + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); + } else { + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down"); + } + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); + service.setInterfaceConfig(iface, ifcg); + } + } catch (Exception e) { + Log.e(TAG, "Error configuring interface " + iface + ", :" + e); + return false; + } + } + } + + return true; + } + + public String[] getTetherableUsbRegexs() { + return mTetherableUsbRegexs; + } + + public String[] getTetherableWifiRegexs() { + return mTetherableWifiRegexs; + } + + public String[] getUpstreamIfaceRegexs() { + return mUpstreamIfaceRegexs; + } + + public boolean isDunRequired() { + return mDunRequired; + } + + public String[] getTetheredIfaces() { + ArrayList<String> list = new ArrayList<String>(); + synchronized (mIfaces) { + Set keys = mIfaces.keySet(); + for (Object key : keys) { + TetherInterfaceSM sm = mIfaces.get(key); + if (sm.isTethered()) { + list.add((String)key); + } + } + } + String[] retVal = new String[list.size()]; + for (int i=0; i < list.size(); i++) { + retVal[i] = list.get(i); + } + return retVal; + } + + public String[] getTetherableIfaces() { + ArrayList<String> list = new ArrayList<String>(); + synchronized (mIfaces) { + Set keys = mIfaces.keySet(); + for (Object key : keys) { + TetherInterfaceSM sm = mIfaces.get(key); + if (sm.isAvailable()) { + list.add((String)key); + } + } + } + String[] retVal = new String[list.size()]; + for (int i=0; i < list.size(); i++) { + retVal[i] = list.get(i); + } + return retVal; + } + + public String[] getErroredIfaces() { + ArrayList<String> list = new ArrayList<String>(); + synchronized (mIfaces) { + Set keys = mIfaces.keySet(); + for (Object key : keys) { + TetherInterfaceSM sm = mIfaces.get(key); + if (sm.isErrored()) { + list.add((String)key); + } + } + } + String[] retVal = new String[list.size()]; + for (int i= 0; i< list.size(); i++) { + retVal[i] = list.get(i); + } + return retVal; + } + + + class TetherInterfaceSM extends HierarchicalStateMachine { + // notification from the master SM that it's not in tether mode + static final int CMD_TETHER_MODE_DEAD = 1; + // request from the user that it wants to tether + static final int CMD_TETHER_REQUESTED = 2; + // request from the user that it wants to untether + static final int CMD_TETHER_UNREQUESTED = 3; + // notification that this interface is down + static final int CMD_INTERFACE_DOWN = 4; + // notification that this interface is up + static final int CMD_INTERFACE_UP = 5; + // notification from the master SM that it had an error turning on cellular dun + static final int CMD_CELL_DUN_ERROR = 6; + // notification from the master SM that it had trouble enabling IP Forwarding + static final int CMD_IP_FORWARDING_ENABLE_ERROR = 7; + // notification from the master SM that it had trouble disabling IP Forwarding + static final int CMD_IP_FORWARDING_DISABLE_ERROR = 8; + // notification from the master SM that it had trouble staring tethering + static final int CMD_START_TETHERING_ERROR = 9; + // notification from the master SM that it had trouble stopping tethering + static final int CMD_STOP_TETHERING_ERROR = 10; + // notification from the master SM that it had trouble setting the DNS forwarders + static final int CMD_SET_DNS_FORWARDERS_ERROR = 11; + // the upstream connection has changed + static final int CMD_TETHER_CONNECTION_CHANGED = 12; + + private HierarchicalState mDefaultState; + + private HierarchicalState mInitialState; + private HierarchicalState mStartingState; + private HierarchicalState mTetheredState; + + private HierarchicalState mUnavailableState; + + private boolean mAvailable; + private boolean mTethered; + int mLastError; + + String mIfaceName; + String mMyUpstreamIfaceName; // may change over time + + boolean mUsb; + + TetherInterfaceSM(String name, Looper looper, boolean usb) { + super(name, looper); + mIfaceName = name; + mUsb = usb; + setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR); + + mInitialState = new InitialState(); + addState(mInitialState); + mStartingState = new StartingState(); + addState(mStartingState); + mTetheredState = new TetheredState(); + addState(mTetheredState); + mUnavailableState = new UnavailableState(); + addState(mUnavailableState); + + setInitialState(mInitialState); + } + + public String toString() { + String res = new String(); + res += mIfaceName + " - "; + HierarchicalState current = getCurrentState(); + if (current == mInitialState) res += "InitialState"; + if (current == mStartingState) res += "StartingState"; + if (current == mTetheredState) res += "TetheredState"; + if (current == mUnavailableState) res += "UnavailableState"; + if (mAvailable) res += " - Available"; + if (mTethered) res += " - Tethered"; + res += " - lastError =" + mLastError; + return res; + } + + public synchronized int getLastError() { + return mLastError; + } + + private synchronized void setLastError(int error) { + mLastError = error; + + if (isErrored()) { + if (mUsb) { + // note everything's been unwound by this point so nothing to do on + // further error.. + Tethering.this.configureUsbIface(false); + } + } + } + + // synchronized between this getter and the following setter + public synchronized boolean isAvailable() { + return mAvailable; + } + + private synchronized void setAvailable(boolean available) { + mAvailable = available; + } + + // synchronized between this getter and the following setter + public synchronized boolean isTethered() { + return mTethered; + } + + private synchronized void setTethered(boolean tethered) { + mTethered = tethered; + } + + // synchronized between this getter and the following setter + public synchronized boolean isErrored() { + return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR); + } + + class InitialState extends HierarchicalState { + @Override + public void enter() { + setAvailable(true); + setTethered(false); + sendTetherStateChangedBroadcast(); + } + + @Override + public boolean processMessage(Message message) { + Log.d(TAG, "InitialState.processMessage what=" + message.what); + boolean retValue = true; + switch (message.what) { + case CMD_TETHER_REQUESTED: + setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR); + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_REQUESTED, + TetherInterfaceSM.this); + transitionTo(mStartingState); + break; + case CMD_INTERFACE_DOWN: + transitionTo(mUnavailableState); + break; + default: + retValue = false; + break; + } + return retValue; + } + } + + class StartingState extends HierarchicalState { + @Override + public void enter() { + setAvailable(false); + if (mUsb) { + if (!Tethering.this.configureUsbIface(true)) { + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED, + TetherInterfaceSM.this); + setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR); + + transitionTo(mInitialState); + return; + } + } + sendTetherStateChangedBroadcast(); + + // Skipping StartingState + transitionTo(mTetheredState); + } + @Override + public boolean processMessage(Message message) { + Log.d(TAG, "StartingState.processMessage what=" + message.what); + boolean retValue = true; + switch (message.what) { + // maybe a parent class? + case CMD_TETHER_UNREQUESTED: + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED, + TetherInterfaceSM.this); + if (mUsb) { + if (!Tethering.this.configureUsbIface(false)) { + setLastErrorAndTransitionToInitialState( + ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR); + break; + } + } + transitionTo(mInitialState); + break; + case CMD_CELL_DUN_ERROR: + case CMD_IP_FORWARDING_ENABLE_ERROR: + case CMD_IP_FORWARDING_DISABLE_ERROR: + case CMD_START_TETHERING_ERROR: + case CMD_STOP_TETHERING_ERROR: + case CMD_SET_DNS_FORWARDERS_ERROR: + setLastErrorAndTransitionToInitialState( + ConnectivityManager.TETHER_ERROR_MASTER_ERROR); + break; + case CMD_INTERFACE_DOWN: + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED, + TetherInterfaceSM.this); + transitionTo(mUnavailableState); + break; + default: + retValue = false; + } + return retValue; + } + } + + class TetheredState extends HierarchicalState { + @Override + public void enter() { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = + INetworkManagementService.Stub.asInterface(b); + try { + service.tetherInterface(mIfaceName); + } catch (Exception e) { + setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR); + + transitionTo(mInitialState); + return; + } + if (mUsb) Tethering.this.enableUsbRndis(true); + Log.d(TAG, "Tethered " + mIfaceName); + setAvailable(false); + setTethered(true); + sendTetherStateChangedBroadcast(); + } + @Override + public void exit() { + if (mUsb) Tethering.this.enableUsbRndis(false); + } + @Override + public boolean processMessage(Message message) { + Log.d(TAG, "TetheredState.processMessage what=" + message.what); + boolean retValue = true; + boolean error = false; + switch (message.what) { + case CMD_TETHER_UNREQUESTED: + case CMD_INTERFACE_DOWN: + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = + INetworkManagementService.Stub.asInterface(b); + if (mMyUpstreamIfaceName != null) { + try { + service.disableNat(mIfaceName, mMyUpstreamIfaceName); + mMyUpstreamIfaceName = null; + } catch (Exception e) { + try { + service.untetherInterface(mIfaceName); + } catch (Exception ee) {} + + setLastErrorAndTransitionToInitialState( + ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR); + break; + } + } + try { + service.untetherInterface(mIfaceName); + } catch (Exception e) { + setLastErrorAndTransitionToInitialState( + ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR); + break; + } + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED, + TetherInterfaceSM.this); + if (message.what == CMD_TETHER_UNREQUESTED) { + if (mUsb) { + if (!Tethering.this.configureUsbIface(false)) { + setLastError( + ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR); + } + } + transitionTo(mInitialState); + } else if (message.what == CMD_INTERFACE_DOWN) { + transitionTo(mUnavailableState); + } + Log.d(TAG, "Untethered " + mIfaceName); + break; + case CMD_TETHER_CONNECTION_CHANGED: + String newUpstreamIfaceName = (String)(message.obj); + b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + service = INetworkManagementService.Stub.asInterface(b); + + if (mMyUpstreamIfaceName != null) { + try { + service.disableNat(mIfaceName, mMyUpstreamIfaceName); + mMyUpstreamIfaceName = null; + } catch (Exception e) { + try { + service.untetherInterface(mIfaceName); + } catch (Exception ee) {} + + setLastErrorAndTransitionToInitialState( + ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR); + break; + } + } + if (newUpstreamIfaceName != null) { + try { + service.enableNat(mIfaceName, newUpstreamIfaceName); + } catch (Exception e) { + try { + service.untetherInterface(mIfaceName); + } catch (Exception ee) {} + + setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR); + transitionTo(mInitialState); + return true; + } + } + mMyUpstreamIfaceName = newUpstreamIfaceName; + break; + case CMD_CELL_DUN_ERROR: + case CMD_IP_FORWARDING_ENABLE_ERROR: + case CMD_IP_FORWARDING_DISABLE_ERROR: + case CMD_START_TETHERING_ERROR: + case CMD_STOP_TETHERING_ERROR: + case CMD_SET_DNS_FORWARDERS_ERROR: + error = true; + // fall through + case CMD_TETHER_MODE_DEAD: + b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + service = INetworkManagementService.Stub.asInterface(b); + if (mMyUpstreamIfaceName != null) { + try { + service.disableNat(mIfaceName, mMyUpstreamIfaceName); + mMyUpstreamIfaceName = null; + } catch (Exception e) { + try { + service.untetherInterface(mIfaceName); + } catch (Exception ee) {} + + setLastErrorAndTransitionToInitialState( + ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR); + break; + } + } + try { + service.untetherInterface(mIfaceName); + } catch (Exception e) { + setLastErrorAndTransitionToInitialState( + ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR); + break; + } + if (error) { + setLastErrorAndTransitionToInitialState( + ConnectivityManager.TETHER_ERROR_MASTER_ERROR); + break; + } + Log.d(TAG, "Tether lost upstream connection " + mIfaceName); + sendTetherStateChangedBroadcast(); + if (mUsb) { + if (!Tethering.this.configureUsbIface(false)) { + setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR); + } + } + transitionTo(mInitialState); + break; + default: + retValue = false; + break; + } + return retValue; + } + } + + class UnavailableState extends HierarchicalState { + @Override + public void enter() { + setAvailable(false); + setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR); + setTethered(false); + sendTetherStateChangedBroadcast(); + } + @Override + public boolean processMessage(Message message) { + boolean retValue = true; + switch (message.what) { + case CMD_INTERFACE_UP: + transitionTo(mInitialState); + break; + default: + retValue = false; + break; + } + return retValue; + } + } + + void setLastErrorAndTransitionToInitialState(int error) { + setLastError(error); + transitionTo(mInitialState); + } + + } + + class TetherMasterSM extends HierarchicalStateMachine { + // an interface SM has requested Tethering + static final int CMD_TETHER_MODE_REQUESTED = 1; + // an interface SM has unrequested Tethering + static final int CMD_TETHER_MODE_UNREQUESTED = 2; + // upstream connection change - do the right thing + static final int CMD_UPSTREAM_CHANGED = 3; + // we received notice that the cellular DUN connection is up + static final int CMD_CELL_CONNECTION_RENEW = 4; + // we don't have a valid upstream conn, check again after a delay + static final int CMD_RETRY_UPSTREAM = 5; + + // This indicates what a timeout event relates to. A state that + // sends itself a delayed timeout event and handles incoming timeout events + // should inc this when it is entered and whenever it sends a new timeout event. + // We do not flush the old ones. + private int mSequenceNumber; + + private HierarchicalState mInitialState; + private HierarchicalState mTetherModeAliveState; + + private HierarchicalState mSetIpForwardingEnabledErrorState; + private HierarchicalState mSetIpForwardingDisabledErrorState; + private HierarchicalState mStartTetheringErrorState; + private HierarchicalState mStopTetheringErrorState; + private HierarchicalState mSetDnsForwardersErrorState; + + private ArrayList mNotifyList; + + private boolean mConnectionRequested = false; + + private String mUpstreamIfaceName = null; + + private static final int UPSTREAM_SETTLE_TIME_MS = 10000; + private static final int CELL_CONNECTION_RENEW_MS = 40000; + + TetherMasterSM(String name, Looper looper) { + super(name, looper); + + //Add states + mInitialState = new InitialState(); + addState(mInitialState); + mTetherModeAliveState = new TetherModeAliveState(); + addState(mTetherModeAliveState); + + mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState(); + addState(mSetIpForwardingEnabledErrorState); + mSetIpForwardingDisabledErrorState = new SetIpForwardingDisabledErrorState(); + addState(mSetIpForwardingDisabledErrorState); + mStartTetheringErrorState = new StartTetheringErrorState(); + addState(mStartTetheringErrorState); + mStopTetheringErrorState = new StopTetheringErrorState(); + addState(mStopTetheringErrorState); + mSetDnsForwardersErrorState = new SetDnsForwardersErrorState(); + addState(mSetDnsForwardersErrorState); + + mNotifyList = new ArrayList(); + setInitialState(mInitialState); + } + + class TetherMasterUtilState extends HierarchicalState { + protected final static boolean TRY_TO_SETUP_MOBILE_CONNECTION = true; + protected final static boolean WAIT_FOR_NETWORK_TO_SETTLE = false; + + @Override + public boolean processMessage(Message m) { + return false; + } + protected int turnOnMobileConnection() { + IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); + int retValue = Phone.APN_REQUEST_FAILED; + try { + retValue = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, + (mDunRequired ? Phone.FEATURE_ENABLE_DUN : Phone.FEATURE_ENABLE_HIPRI), + new Binder()); + } catch (Exception e) { + } + switch (retValue) { + case Phone.APN_ALREADY_ACTIVE: + case Phone.APN_REQUEST_STARTED: + sendMessageDelayed(CMD_CELL_CONNECTION_RENEW, CELL_CONNECTION_RENEW_MS); + mConnectionRequested = true; + break; + case Phone.APN_REQUEST_FAILED: + default: + mConnectionRequested = false; + break; + } + + return retValue; + } + protected boolean turnOffMobileConnection() { + if (mConnectionRequested) { + IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + IConnectivityManager service = + IConnectivityManager.Stub.asInterface(b); + try { + service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, + (mDunRequired? Phone.FEATURE_ENABLE_DUN : + Phone.FEATURE_ENABLE_HIPRI)); + } catch (Exception e) { + return false; + } + mConnectionRequested = false; + } + return true; + } + protected boolean turnOnMasterTetherSettings() { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = + INetworkManagementService.Stub.asInterface(b); + try { + service.setIpForwardingEnabled(true); + } catch (Exception e) { + transitionTo(mSetIpForwardingEnabledErrorState); + return false; + } + try { + service.startTethering(mDhcpRange); + } catch (Exception e) { + transitionTo(mStartTetheringErrorState); + return false; + } + try { + service.setDnsForwarders(mDnsServers); + } catch (Exception e) { + transitionTo(mSetDnsForwardersErrorState); + return false; + } + return true; + } + protected boolean turnOffMasterTetherSettings() { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = + INetworkManagementService.Stub.asInterface(b); + try { + service.stopTethering(); + } catch (Exception e) { + transitionTo(mStopTetheringErrorState); + return false; + } + try { + service.setIpForwardingEnabled(false); + } catch (Exception e) { + transitionTo(mSetIpForwardingDisabledErrorState); + return false; + } + transitionTo(mInitialState); + return true; + } + protected String findActiveUpstreamIface() { + // check for what iface we can use - if none found switch to error. + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + + String[] ifaces = new String[0]; + try { + ifaces = service.listInterfaces(); + } catch (Exception e) { + Log.e(TAG, "Error listing Interfaces :" + e); + return null; + } + + for (String iface : ifaces) { + for (String regex : mUpstreamIfaceRegexs) { + if (iface.matches(regex)) { + // verify it is up! + InterfaceConfiguration ifcg = null; + try { + ifcg = service.getInterfaceConfig(iface); + } catch (Exception e) { + Log.e(TAG, "Error getting iface config :" + e); + // ignore - try next + continue; + } + if (ifcg.interfaceFlags.contains("up")) { + return iface; + } + } + } + } + return null; + } + protected void chooseUpstreamType(boolean tryCell) { + // decide if the current upstream is good or not and if not + // do something about it (start up DUN if required or HiPri if not) + String iface = findActiveUpstreamIface(); + IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b); + mConnectionRequested = false; + Log.d(TAG, "chooseUpstreamType(" + tryCell + "), dunRequired =" + + mDunRequired + ", iface=" + iface); + if (iface != null) { + try { + if (mDunRequired) { + // check if Dun is on - we can use that + NetworkInfo info = cm.getNetworkInfo( + ConnectivityManager.TYPE_MOBILE_DUN); + if (info.isConnected()) { + Log.d(TAG, "setting dun ifacename =" + iface); + // even if we're already connected - it may be somebody else's + // refcount, so add our own + turnOnMobileConnection(); + } else { + // verify the iface is not the default mobile - can't use that! + info = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + if (info.isConnected()) { + iface = null; // can't accept this one + } + } + } else { + Log.d(TAG, "checking if hipri brought us this connection"); + NetworkInfo info = cm.getNetworkInfo( + ConnectivityManager.TYPE_MOBILE_HIPRI); + if (info.isConnected()) { + Log.d(TAG, "yes - hipri in use"); + // even if we're already connected - it may be sombody else's + // refcount, so add our own + turnOnMobileConnection(); + } + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException calling ConnectivityManager " + e); + iface = null; + } + } + // may have been set to null in the if above + if (iface == null ) { + if (tryCell == TRY_TO_SETUP_MOBILE_CONNECTION) { + turnOnMobileConnection(); + } + // wait for things to settle and retry + sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS); + } + notifyTetheredOfNewUpstreamIface(iface); + } + protected void notifyTetheredOfNewUpstreamIface(String ifaceName) { + Log.d(TAG, "notifying tethered with iface =" + ifaceName); + mUpstreamIfaceName = ifaceName; + for (Object o : mNotifyList) { + TetherInterfaceSM sm = (TetherInterfaceSM)o; + sm.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED, + ifaceName); + } + } + } + + class InitialState extends TetherMasterUtilState { + @Override + public void enter() { + mConnectionRequested = false; + } + @Override + public boolean processMessage(Message message) { + Log.d(TAG, "MasterInitialState.processMessage what=" + message.what); + boolean retValue = true; + switch (message.what) { + case CMD_TETHER_MODE_REQUESTED: + TetherInterfaceSM who = (TetherInterfaceSM)message.obj; + Log.d(TAG, "Tether Mode requested by " + who.toString()); + mNotifyList.add(who); + transitionTo(mTetherModeAliveState); + break; + case CMD_TETHER_MODE_UNREQUESTED: + who = (TetherInterfaceSM)message.obj; + Log.d(TAG, "Tether Mode unrequested by " + who.toString()); + int index = mNotifyList.indexOf(who); + if (index != -1) { + mNotifyList.remove(who); + } + break; + default: + retValue = false; + break; + } + return retValue; + } + } + + class TetherModeAliveState extends TetherMasterUtilState { + boolean mTryCell = WAIT_FOR_NETWORK_TO_SETTLE; + @Override + public void enter() { + mTryCell = WAIT_FOR_NETWORK_TO_SETTLE; // first pass lets just see what we have. + chooseUpstreamType(mTryCell); + mTryCell = !mTryCell; + turnOnMasterTetherSettings(); // may transition us out + } + @Override + public void exit() { + turnOffMobileConnection(); + notifyTetheredOfNewUpstreamIface(null); + } + @Override + public boolean processMessage(Message message) { + Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what); + boolean retValue = true; + switch (message.what) { + case CMD_TETHER_MODE_REQUESTED: + TetherInterfaceSM who = (TetherInterfaceSM)message.obj; + mNotifyList.add(who); + who.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED, + mUpstreamIfaceName); + break; + case CMD_TETHER_MODE_UNREQUESTED: + who = (TetherInterfaceSM)message.obj; + int index = mNotifyList.indexOf(who); + if (index != -1) { + mNotifyList.remove(index); + if (mNotifyList.isEmpty()) { + turnOffMasterTetherSettings(); // transitions appropriately + } + } + break; + case CMD_UPSTREAM_CHANGED: + mTryCell = WAIT_FOR_NETWORK_TO_SETTLE; + chooseUpstreamType(mTryCell); + mTryCell = !mTryCell; + break; + case CMD_CELL_CONNECTION_RENEW: + // make sure we're still using a requested connection - may have found + // wifi or something since then. + if (mConnectionRequested) { + Log.d(TAG, "renewing mobile connection - requeuing for another " + + CELL_CONNECTION_RENEW_MS + "ms"); + turnOnMobileConnection(); + } + break; + case CMD_RETRY_UPSTREAM: + chooseUpstreamType(mTryCell); + mTryCell = !mTryCell; + break; + default: + retValue = false; + break; + } + return retValue; + } + } + + class ErrorState extends HierarchicalState { + int mErrorNotification; + @Override + public boolean processMessage(Message message) { + boolean retValue = true; + switch (message.what) { + case CMD_TETHER_MODE_REQUESTED: + TetherInterfaceSM who = (TetherInterfaceSM)message.obj; + who.sendMessage(mErrorNotification); + break; + default: + retValue = false; + } + return retValue; + } + void notify(int msgType) { + mErrorNotification = msgType; + for (Object o : mNotifyList) { + TetherInterfaceSM sm = (TetherInterfaceSM)o; + sm.sendMessage(msgType); + } + } + + } + class SetIpForwardingEnabledErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error in setIpForwardingEnabled"); + notify(TetherInterfaceSM.CMD_IP_FORWARDING_ENABLE_ERROR); + } + } + + class SetIpForwardingDisabledErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error in setIpForwardingDisabled"); + notify(TetherInterfaceSM.CMD_IP_FORWARDING_DISABLE_ERROR); + } + } + + class StartTetheringErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error in startTethering"); + notify(TetherInterfaceSM.CMD_START_TETHERING_ERROR); + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = + INetworkManagementService.Stub.asInterface(b); + try { + service.setIpForwardingEnabled(false); + } catch (Exception e) {} + } + } + + class StopTetheringErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error in stopTethering"); + notify(TetherInterfaceSM.CMD_STOP_TETHERING_ERROR); + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = + INetworkManagementService.Stub.asInterface(b); + try { + service.setIpForwardingEnabled(false); + } catch (Exception e) {} + } + } + + class SetDnsForwardersErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error in setDnsForwarders"); + notify(TetherInterfaceSM.CMD_SET_DNS_FORWARDERS_ERROR); + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = + INetworkManagementService.Stub.asInterface(b); + try { + service.stopTethering(); + } catch (Exception e) {} + try { + service.setIpForwardingEnabled(false); + } catch (Exception e) {} + } + } + } + + 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 ConnectivityService.Tether " + + "from from pid=" + Binder.getCallingPid() + ", uid=" + + Binder.getCallingUid()); + return; + } + + pw.println(); + pw.println("Tether state:"); + synchronized (mIfaces) { + for (Object o : mIfaces.values()) { + pw.println(" "+o.toString()); + } + } + pw.println(); + return; + } +} diff --git a/services/java/com/android/server/status/AnimatedImageView.java b/services/java/com/android/server/status/AnimatedImageView.java index cd581c4..97df065 100644 --- a/services/java/com/android/server/status/AnimatedImageView.java +++ b/services/java/com/android/server/status/AnimatedImageView.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.content.Context; diff --git a/services/java/com/android/server/status/CloseDragHandle.java b/services/java/com/android/server/status/CloseDragHandle.java index fabf2ba..ad1ac4d 100644 --- a/services/java/com/android/server/status/CloseDragHandle.java +++ b/services/java/com/android/server/status/CloseDragHandle.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.content.Context; diff --git a/services/java/com/android/server/status/DateView.java b/services/java/com/android/server/status/DateView.java index 78bfd5e..c04fb45 100644 --- a/services/java/com/android/server/status/DateView.java +++ b/services/java/com/android/server/status/DateView.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.content.BroadcastReceiver; @@ -5,7 +21,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.util.AttributeSet; -import android.util.Log; +import android.util.Slog; import android.widget.TextView; import android.view.MotionEvent; diff --git a/services/java/com/android/server/status/ExpandedView.java b/services/java/com/android/server/status/ExpandedView.java index d0f14cb..cb37f90 100644 --- a/services/java/com/android/server/status/ExpandedView.java +++ b/services/java/com/android/server/status/ExpandedView.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.content.Context; @@ -7,21 +23,15 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.WindowManager; import android.widget.LinearLayout; -import android.util.Log; +import android.util.Slog; public class ExpandedView extends LinearLayout { - final Display mDisplay; StatusBarService mService; - boolean mTracking; - int mStartX, mStartY; - int mMaxHeight = 0; int mPrevHeight = -1; public ExpandedView(Context context, AttributeSet attrs) { super(context, attrs); - mDisplay = ((WindowManager)context.getSystemService( - Context.WINDOW_SERVICE)).getDefaultDisplay(); } @Override @@ -36,26 +46,13 @@ public class ExpandedView extends LinearLayout { } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, - MeasureSpec.makeMeasureSpec(mMaxHeight, MeasureSpec.AT_MOST)); - } - - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); int height = bottom - top; if (height != mPrevHeight) { - //Log.d(StatusBarService.TAG, "height changed old=" + mPrevHeight + " new=" + height); + //Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight + " new=" + height); mPrevHeight = height; mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE); } } - - void setMaxHeight(int h) { - if (h != mMaxHeight) { - mMaxHeight = h; - requestLayout(); - } - } } diff --git a/services/java/com/android/server/status/FixedSizeDrawable.java b/services/java/com/android/server/status/FixedSizeDrawable.java index fe5abca..dbfcb2c 100644 --- a/services/java/com/android/server/status/FixedSizeDrawable.java +++ b/services/java/com/android/server/status/FixedSizeDrawable.java @@ -1,10 +1,26 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.graphics.drawable.Drawable; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Rect; -import android.util.Log; +import android.util.Slog; class FixedSizeDrawable extends Drawable { Drawable mDrawable; diff --git a/services/java/com/android/server/status/IconData.java b/services/java/com/android/server/status/IconData.java index 8a61eb5..fd226f9 100644 --- a/services/java/com/android/server/status/IconData.java +++ b/services/java/com/android/server/status/IconData.java @@ -1,6 +1,22 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; -import android.util.Log; +import android.util.Slog; public class IconData { /** diff --git a/services/java/com/android/server/status/IconMerger.java b/services/java/com/android/server/status/IconMerger.java index 5b80638..aa702ae 100644 --- a/services/java/com/android/server/status/IconMerger.java +++ b/services/java/com/android/server/status/IconMerger.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.content.Context; diff --git a/services/java/com/android/server/status/LatestItemView.java b/services/java/com/android/server/status/LatestItemView.java index a47f6ad..fe8d164 100644 --- a/services/java/com/android/server/status/LatestItemView.java +++ b/services/java/com/android/server/status/LatestItemView.java @@ -1,8 +1,24 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.content.Context; import android.util.AttributeSet; -import android.util.Log; +import android.util.Slog; import android.view.MotionEvent; import android.widget.FrameLayout; diff --git a/services/java/com/android/server/status/NotificationData.java b/services/java/com/android/server/status/NotificationData.java index 0a3411a..71f01ca 100644 --- a/services/java/com/android/server/status/NotificationData.java +++ b/services/java/com/android/server/status/NotificationData.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.app.PendingIntent; @@ -19,7 +35,7 @@ public class NotificationData { public PendingIntent deleteIntent; public String toString() { - return "NotificationData(package=" + pkg + " tickerText=" + tickerText + return "NotificationData(package=" + pkg + " id=" + id + " tickerText=" + tickerText + " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent + " deleteIntent=" + deleteIntent + " clearable=" + clearable diff --git a/services/java/com/android/server/status/NotificationLinearLayout.java b/services/java/com/android/server/status/NotificationLinearLayout.java index ac2e44d..2fdf956 100644 --- a/services/java/com/android/server/status/NotificationLinearLayout.java +++ b/services/java/com/android/server/status/NotificationLinearLayout.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.content.Context; diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java index 6229292..1bb56a7 100644 --- a/services/java/com/android/server/status/NotificationViewList.java +++ b/services/java/com/android/server/status/NotificationViewList.java @@ -1,7 +1,23 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.os.IBinder; -import android.util.Log; +import android.util.Slog; import android.view.View; import java.util.ArrayList; @@ -104,10 +120,25 @@ class NotificationViewList { return null; } - // gets the index of the notification in its expanded parent view + // gets the index of the notification's view in its expanded parent view int getExpandedIndex(StatusBarNotification notification) { ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest; - return list.size() - indexForKey(list, notification.key) - 1; + final IBinder key = notification.key; + int index = 0; + // (the view order is backwards from this list order) + for (int i=list.size()-1; i>=0; i--) { + StatusBarNotification item = list.get(i); + if (item.key == key) { + return index; + } + if (item.view != null) { + index++; + } + } + Slog.e(StatusBarService.TAG, "Couldn't find notification in NotificationViewList."); + Slog.e(StatusBarService.TAG, "notification=" + notification); + dump(notification); + return 0; } void clearViews() { @@ -142,6 +173,12 @@ class NotificationViewList { } void add(StatusBarNotification notification) { + if (StatusBarService.SPEW) { + Slog.d(StatusBarService.TAG, "before add NotificationViewList" + + " notification.data.ongoingEvent=" + notification.data.ongoingEvent); + dump(notification); + } + ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest; long when = notification.data.when; final int N = list.size(); @@ -156,19 +193,31 @@ class NotificationViewList { list.add(index, notification); if (StatusBarService.SPEW) { + Slog.d(StatusBarService.TAG, "after add NotificationViewList index=" + index); + dump(notification); + } + } + + void dump(StatusBarNotification notification) { + if (StatusBarService.SPEW) { + boolean showTime = false; String s = ""; for (int i=0; i<mOngoing.size(); i++) { StatusBarNotification that = mOngoing.get(i); if (that.key == notification.key) { s += "["; } - s += that.data.when; + if (showTime) { + s += that.data.when; + } else { + s += that.data.pkg + "/" + that.data.id + "/" + that.view; + } if (that.key == notification.key) { s += "]"; } s += " "; } - Log.d(StatusBarService.TAG, "NotificationViewList ongoing index=" + index + ": " + s); + Slog.d(StatusBarService.TAG, "NotificationViewList ongoing: " + s); s = ""; for (int i=0; i<mLatest.size(); i++) { @@ -176,13 +225,17 @@ class NotificationViewList { if (that.key == notification.key) { s += "["; } - s += that.data.when; + if (showTime) { + s += that.data.when; + } else { + s += that.data.pkg + "/" + that.data.id + "/" + that.view; + } if (that.key == notification.key) { s += "]"; } s += " "; } - Log.d(StatusBarService.TAG, "NotificationViewList latest index=" + index + ": " + s); + Slog.d(StatusBarService.TAG, "NotificationViewList latest: " + s); } } diff --git a/services/java/com/android/server/status/StatusBarException.java b/services/java/com/android/server/status/StatusBarException.java index 8e93ca7..be58f59 100644 --- a/services/java/com/android/server/status/StatusBarException.java +++ b/services/java/com/android/server/status/StatusBarException.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; public class StatusBarException extends RuntimeException { diff --git a/services/java/com/android/server/status/StatusBarIcon.java b/services/java/com/android/server/status/StatusBarIcon.java index 857784b..6f8b8a8 100644 --- a/services/java/com/android/server/status/StatusBarIcon.java +++ b/services/java/com/android/server/status/StatusBarIcon.java @@ -1,18 +1,32 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Typeface; -import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; import android.text.TextUtils; -import android.util.Log; +import android.util.Slog; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -41,7 +55,7 @@ class StatusBarIcon { mTextView = t; LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.FILL_PARENT); + LinearLayout.LayoutParams.MATCH_PARENT); t.setTextSize(16); t.setTextColor(0xff000000); t.setTypeface(Typeface.DEFAULT_BOLD); @@ -142,7 +156,7 @@ class StatusBarIcon { try { r = context.getPackageManager().getResourcesForApplication(data.iconPackage); } catch (PackageManager.NameNotFoundException ex) { - Log.e(StatusBarService.TAG, "Icon package not found: " + data.iconPackage, ex); + Slog.e(StatusBarService.TAG, "Icon package not found: " + data.iconPackage, ex); return null; } } else { @@ -150,14 +164,14 @@ class StatusBarIcon { } if (data.iconId == 0) { - Log.w(StatusBarService.TAG, "No icon ID for slot " + data.slot); + Slog.w(StatusBarService.TAG, "No icon ID for slot " + data.slot); return null; } try { return r.getDrawable(data.iconId); } catch (RuntimeException e) { - Log.w(StatusBarService.TAG, "Icon not found in " + Slog.w(StatusBarService.TAG, "Icon not found in " + (data.iconPackage != null ? data.iconId : "<system>") + ": " + Integer.toHexString(data.iconId)); } diff --git a/services/java/com/android/server/status/StatusBarNotification.java b/services/java/com/android/server/status/StatusBarNotification.java index 4636cba..e5773f7 100644 --- a/services/java/com/android/server/status/StatusBarNotification.java +++ b/services/java/com/android/server/status/StatusBarNotification.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.os.IBinder; diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java index bee0930..94d1cb4 100644 --- a/services/java/com/android/server/status/StatusBarPolicy.java +++ b/services/java/com/android/server/status/StatusBarPolicy.java @@ -22,6 +22,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothPbap; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -30,20 +31,27 @@ import android.content.res.TypedArray; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; import android.media.AudioManager; +import android.media.Ringtone; +import android.media.RingtoneManager; import android.net.NetworkInfo; +import android.net.Uri; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; +import android.os.storage.StorageManager; import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.text.format.DateFormat; -import android.util.Log; +import android.text.style.RelativeSizeSpan; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.util.Slog; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -61,6 +69,7 @@ import com.android.internal.telephony.cdma.EriInfo; import com.android.internal.telephony.cdma.TtyIntent; import com.android.server.am.BatteryStatsService; +import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.TimeZone; @@ -84,9 +93,14 @@ public class StatusBarPolicy { // clock private Calendar mCalendar; + private String mClockFormatString; + private SimpleDateFormat mClockFormat; private IBinder mClockIcon; private IconData mClockData; + // storage + private StorageManager mStorageManager; + // battery private IBinder mBatteryIcon; private IconData mBatteryData; @@ -99,6 +113,7 @@ public class StatusBarPolicy { private int mBatteryViewSequence; private boolean mBatteryShowLowOnEndCall = false; private static final boolean SHOW_LOW_BATTERY_WARNING = true; + private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true; // phone private TelephonyManager mPhone; @@ -346,6 +361,9 @@ public class StatusBarPolicy { else if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) { String tz = intent.getStringExtra("time-zone"); mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz)); + if (mClockFormat != null) { + mClockFormat.setTimeZone(mCalendar.getTimeZone()); + } updateClock(); } else if (action.equals(Intent.ACTION_ALARM_CHANGED)) { @@ -401,6 +419,11 @@ public class StatusBarPolicy { mClockIcon = service.addIcon(mClockData, null); updateClock(); + // storage + mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); + mStorageManager.registerListener( + new com.android.server.status.StorageNotification(context)); + // battery mBatteryData = IconData.makeIcon("battery", null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 0); @@ -532,10 +555,80 @@ public class StatusBarPolicy { sInstance = new StatusBarPolicy(context, service); } + private final CharSequence getSmallTime() { + boolean b24 = DateFormat.is24HourFormat(mContext); + int res; + + if (b24) { + res = R.string.twenty_four_hour_time_format; + } else { + res = R.string.twelve_hour_time_format; + } + + final char MAGIC1 = '\uEF00'; + final char MAGIC2 = '\uEF01'; + + SimpleDateFormat sdf; + String format = mContext.getString(res); + if (!format.equals(mClockFormatString)) { + /* + * Search for an unquoted "a" in the format string, so we can + * add dummy characters around it to let us find it again after + * formatting and change its size. + */ + int a = -1; + boolean quoted = false; + for (int i = 0; i < format.length(); i++) { + char c = format.charAt(i); + + if (c == '\'') { + quoted = !quoted; + } + + if (!quoted && c == 'a') { + a = i; + break; + } + } + + if (a >= 0) { + // Move a back so any whitespace before the AM/PM is also in the alternate size. + final int b = a; + while (a > 0 && Character.isWhitespace(format.charAt(a-1))) { + a--; + } + format = format.substring(0, a) + MAGIC1 + format.substring(a, b) + + "a" + MAGIC2 + format.substring(b + 1); + } + + mClockFormat = sdf = new SimpleDateFormat(format); + mClockFormatString = format; + } else { + sdf = mClockFormat; + } + String result = sdf.format(mCalendar.getTime()); + + int magic1 = result.indexOf(MAGIC1); + int magic2 = result.indexOf(MAGIC2); + + if (magic1 >= 0 && magic2 > magic1) { + SpannableStringBuilder formatted = new SpannableStringBuilder(result); + + formatted.setSpan(new RelativeSizeSpan(0.7f), magic1, magic2, + Spannable.SPAN_EXCLUSIVE_INCLUSIVE); + + formatted.delete(magic2, magic2 + 1); + formatted.delete(magic1, magic1 + 1); + + return formatted; + } else { + return result; + } + } + private final void updateClock() { mCalendar.setTimeInMillis(System.currentTimeMillis()); - mClockData.text = DateFormat.getTimeFormat(mContext) - .format(mCalendar.getTime()); + mClockData.text = getSmallTime(); mService.updateIcon(mClockIcon, mClockData, null); } @@ -560,7 +653,7 @@ public class StatusBarPolicy { boolean plugged = intent.getIntExtra("plugged", 0) != 0; int level = intent.getIntExtra("level", -1); if (false) { - Log.d(TAG, "updateBattery level=" + level + Slog.d(TAG, "updateBattery level=" + level + " plugged=" + plugged + " mBatteryPlugged=" + mBatteryPlugged + " mBatteryLevel=" + mBatteryLevel @@ -589,19 +682,19 @@ public class StatusBarPolicy { } */ if (false) { - Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level); + Slog.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level); } } private void onBatteryLow(Intent intent) { if (SHOW_LOW_BATTERY_WARNING) { if (false) { - Log.d(TAG, "mPhoneState=" + mPhoneState + Slog.d(TAG, "mPhoneState=" + mPhoneState + " mLowBatteryDialog=" + mLowBatteryDialog + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall); } - if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) { + if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) { showLowBatteryWarning(); } else { mBatteryShowLowOnEndCall = true; @@ -725,12 +818,30 @@ public class StatusBarPolicy { d.show(); mLowBatteryDialog = d; } + + final ContentResolver cr = mContext.getContentResolver(); + if (Settings.System.getInt(cr, + Settings.System.POWER_SOUNDS_ENABLED, 1) == 1) + { + final String soundPath = Settings.System.getString(cr, + Settings.System.LOW_BATTERY_SOUND); + if (soundPath != null) { + final Uri soundUri = Uri.parse("file://" + soundPath); + if (soundUri != null) { + final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); + if (sfx != null) { + sfx.setStreamType(AudioManager.STREAM_SYSTEM); + sfx.play(); + } + } + } + } } private final void updateCallState(int state) { mPhoneState = state; if (false) { - Log.d(TAG, "mPhoneState=" + mPhoneState + Slog.d(TAG, "mPhoneState=" + mPhoneState + " mLowBatteryDialog=" + mLowBatteryDialog + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall); } @@ -864,8 +975,9 @@ public class StatusBarPolicy { int iconLevel = -1; int[] iconList; - if (!hasService()) { - //Log.d(TAG, "updateSignalStrength: no service"); + // Display signal strength while in "emergency calls only" mode + if (!hasService() && !mServiceState.isEmergencyOnly()) { + //Slog.d(TAG, "updateSignalStrength: no service"); if (Settings.System.getInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1) { mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_flightmode; @@ -883,10 +995,10 @@ public class StatusBarPolicy { // asu = 0 (-113dB or less) is very weak // signal, its better to show 0 bars to the user in such cases. // asu = 99 is a special case, where the signal strength is unknown. - if (asu <= 0 || asu == 99) iconLevel = 0; - else if (asu >= 16) iconLevel = 4; + if (asu <= 2 || asu == 99) iconLevel = 0; + else if (asu >= 12) iconLevel = 4; else if (asu >= 8) iconLevel = 3; - else if (asu >= 4) iconLevel = 2; + else if (asu >= 5) iconLevel = 2; else iconLevel = 1; // Though mPhone is a Manager, this call is not an IPC @@ -904,7 +1016,7 @@ public class StatusBarPolicy { if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){ iconLevel = getEvdoLevel(); if (false) { - Log.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel()); + Slog.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel()); } } else { iconLevel = getCdmaLevel(); @@ -1067,7 +1179,7 @@ public class StatusBarPolicy { final int ringerMode = audioManager.getRingerMode(); final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT || ringerMode == AudioManager.RINGER_MODE_VIBRATE; - final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER) + final int iconId = (ringerMode == AudioManager.RINGER_MODE_VIBRATE) ? com.android.internal.R.drawable.stat_sys_ringer_vibrate : com.android.internal.R.drawable.stat_sys_ringer_silent; @@ -1198,16 +1310,16 @@ public class StatusBarPolicy { final String action = intent.getAction(); final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false); - if (false) Log.v(TAG, "updateTTY: enabled: " + enabled); + if (false) Slog.v(TAG, "updateTTY: enabled: " + enabled); if (enabled) { // TTY is on - if (false) Log.v(TAG, "updateTTY: set TTY on"); + if (false) Slog.v(TAG, "updateTTY: set TTY on"); mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null); mService.setIconVisibility(mTTYModeIcon, true); } else { // TTY is off - if (false) Log.v(TAG, "updateTTY: set TTY off"); + if (false) Slog.v(TAG, "updateTTY: set TTY off"); mService.setIconVisibility(mTTYModeIcon, false); } } @@ -1228,17 +1340,17 @@ public class StatusBarPolicy { int iconMode = state.getCdmaEriIconMode(); if (iconIndex == -1) { - Log.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update"); + Slog.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update"); return; } if (iconMode == -1) { - Log.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update"); + Slog.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update"); return; } if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) { - if (false) Log.v(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon"); + if (false) Slog.v(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon"); mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false); return; } diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java index 34921d6..93c8d34 100644 --- a/services/java/com/android/server/status/StatusBarService.java +++ b/services/java/com/android/server/status/StatusBarService.java @@ -41,7 +41,7 @@ import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.provider.Telephony; -import android.util.Log; +import android.util.Slog; import android.view.Display; import android.view.Gravity; import android.view.KeyEvent; @@ -116,7 +116,7 @@ public class StatusBarService extends IStatusBar.Stub IBinder token; public void binderDied() { - Log.i(TAG, "binder died for pkg=" + pkg); + Slog.i(TAG, "binder died for pkg=" + pkg); disable(0, token, pkg); token.unlinkToDeath(this, 0); } @@ -153,6 +153,7 @@ public class StatusBarService extends IStatusBar.Stub StatusBarView mStatusBarView; int mPixelFormat; H mHandler = new H(); + Object mQueueLock = new Object(); ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>(); NotificationCallbacks mNotificationCallbacks; @@ -187,8 +188,9 @@ public class StatusBarService extends IStatusBar.Stub TextView mSpnLabel; TextView mPlmnLabel; TextView mClearButton; + View mExpandedContents; CloseDragHandle mCloseView; - int[] mCloseLocation = new int[2]; + int[] mPositionTmp = new int[2]; boolean mExpanded; boolean mExpandedVisible; @@ -198,7 +200,7 @@ public class StatusBarService extends IStatusBar.Stub // the tracker view TrackingView mTrackingView; WindowManager.LayoutParams mTrackingParams; - int mTrackingPosition; + int mTrackingPosition; // the position of the top of the tracking view. // ticker private Ticker mTicker; @@ -206,6 +208,7 @@ public class StatusBarService extends IStatusBar.Stub private boolean mTicking; // Tracking finger for opening/closing. + int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore boolean mTracking; VelocityTracker mVelocityTracker; @@ -273,6 +276,7 @@ public class StatusBarService extends IStatusBar.Stub mExpandedDialog = new ExpandedDialog(context); mExpandedView = expanded; + mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout); mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle); mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems); mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle); @@ -299,6 +303,8 @@ public class StatusBarService extends IStatusBar.Stub mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close); mCloseView.mService = this; + mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); + // add the more icon for the notifications IconData moreData = IconData.makeIcon(null, context.getPackageName(), R.drawable.stat_notify_more, 0, 42); @@ -329,7 +335,7 @@ public class StatusBarService extends IStatusBar.Stub public void systemReady() { final StatusBarView view = mStatusBarView; WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, view.getContext().getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height), WindowManager.LayoutParams.TYPE_STATUS_BAR, @@ -435,7 +441,7 @@ public class StatusBarService extends IStatusBar.Stub } private void addPendingOp(int code, IBinder key, IconData data, NotificationData n, int i) { - synchronized (mQueue) { + synchronized (mQueueLock) { PendingOp op = new PendingOp(); op.key = key; op.code = code; @@ -450,7 +456,7 @@ public class StatusBarService extends IStatusBar.Stub } private void addPendingOp(int code, IBinder key, boolean visible) { - synchronized (mQueue) { + synchronized (mQueueLock) { PendingOp op = new PendingOp(); op.key = key; op.code = code; @@ -463,7 +469,7 @@ public class StatusBarService extends IStatusBar.Stub } private void addPendingOp(int code, int integer) { - synchronized (mQueue) { + synchronized (mQueueLock) { PendingOp op = new PendingOp(); op.code = code; op.integer = integer; @@ -477,7 +483,7 @@ public class StatusBarService extends IStatusBar.Stub // lock on mDisableRecords void manageDisableListLocked(int what, IBinder token, String pkg) { if (SPEW) { - Log.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what) + Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what) + " pkg=" + pkg); } // update the list @@ -552,100 +558,105 @@ public class StatusBarService extends IStatusBar.Stub doRevealAnimation(); return; } - synchronized (mQueue) { - boolean wasExpanded = mExpanded; - - // for each one in the queue, find all of the ones with the same key - // and collapse that down into a final op and/or call to setVisibility, etc - boolean expand = wasExpanded; - boolean doExpand = false; - boolean doDisable = false; - int disableWhat = 0; - int N = mQueue.size(); - while (N > 0) { - PendingOp op = mQueue.get(0); - boolean doOp = false; - boolean visible = false; - boolean doVisibility = false; - if (op.code == OP_SET_VISIBLE) { - doVisibility = true; - visible = op.visible; - } - else if (op.code == OP_EXPAND) { - doExpand = true; - expand = op.visible; - } - else if (op.code == OP_TOGGLE) { - doExpand = true; - expand = !expand; - } - else { - doOp = true; - } - if (alwaysHandle(op.code)) { - // coalesce these - for (int i=1; i<N; i++) { - PendingOp o = mQueue.get(i); - if (!alwaysHandle(o.code) && o.key == op.key) { - if (o.code == OP_SET_VISIBLE) { - visible = o.visible; - doVisibility = true; - } - else if (o.code == OP_EXPAND) { - expand = o.visible; - doExpand = true; - } - else { - op.code = o.code; - op.iconData = o.iconData; - op.notificationData = o.notificationData; - } - mQueue.remove(i); - i--; - N--; - } - } - } + ArrayList<PendingOp> queue; + synchronized (mQueueLock) { + queue = mQueue; + mQueue = new ArrayList<PendingOp>(); + } + + boolean wasExpanded = mExpanded; + + // for each one in the queue, find all of the ones with the same key + // and collapse that down into a final op and/or call to setVisibility, etc + boolean expand = wasExpanded; + boolean doExpand = false; + boolean doDisable = false; + int disableWhat = 0; + int N = queue.size(); + while (N > 0) { + PendingOp op = queue.get(0); + boolean doOp = false; + boolean visible = false; + boolean doVisibility = false; + if (op.code == OP_SET_VISIBLE) { + doVisibility = true; + visible = op.visible; + } + else if (op.code == OP_EXPAND) { + doExpand = true; + expand = op.visible; + } + else if (op.code == OP_TOGGLE) { + doExpand = true; + expand = !expand; + } + else { + doOp = true; + } - mQueue.remove(0); - N--; - - if (doOp) { - switch (op.code) { - case OP_ADD_ICON: - case OP_UPDATE_ICON: - performAddUpdateIcon(op.key, op.iconData, op.notificationData); - break; - case OP_REMOVE_ICON: - performRemoveIcon(op.key); - break; - case OP_DISABLE: - doDisable = true; - disableWhat = op.integer; - break; + if (alwaysHandle(op.code)) { + // coalesce these + for (int i=1; i<N; i++) { + PendingOp o = queue.get(i); + if (!alwaysHandle(o.code) && o.key == op.key) { + if (o.code == OP_SET_VISIBLE) { + visible = o.visible; + doVisibility = true; + } + else if (o.code == OP_EXPAND) { + expand = o.visible; + doExpand = true; + } + else { + op.code = o.code; + op.iconData = o.iconData; + op.notificationData = o.notificationData; + } + queue.remove(i); + i--; + N--; } } - if (doVisibility && op.code != OP_REMOVE_ICON) { - performSetIconVisibility(op.key, visible); - } } - if (mQueue.size() != 0) { - throw new RuntimeException("Assertion failed: mQueue.size=" + mQueue.size()); - } - if (doExpand) { - // this is last so that we capture all of the pending changes before doing it - if (expand) { - animateExpand(); - } else { - animateCollapse(); + queue.remove(0); + N--; + + if (doOp) { + switch (op.code) { + case OP_ADD_ICON: + case OP_UPDATE_ICON: + performAddUpdateIcon(op.key, op.iconData, op.notificationData); + break; + case OP_REMOVE_ICON: + performRemoveIcon(op.key); + break; + case OP_DISABLE: + doDisable = true; + disableWhat = op.integer; + break; } } - if (doDisable) { - performDisableActions(disableWhat); + if (doVisibility && op.code != OP_REMOVE_ICON) { + performSetIconVisibility(op.key, visible); } } + + if (queue.size() != 0) { + throw new RuntimeException("Assertion failed: queue.size=" + queue.size()); + } + if (doExpand) { + // this is last so that we capture all of the pending changes before doing it + if (expand) { + animateExpand(); + } else { + animateCollapse(); + } + } + if (doDisable) { + performDisableActions(disableWhat); + } } } @@ -656,7 +667,7 @@ public class StatusBarService extends IStatusBar.Stub /* private */ void performAddUpdateIcon(IBinder key, IconData data, NotificationData n) throws StatusBarException { if (SPEW) { - Log.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key); + Slog.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key); } // notification if (n != null) { @@ -687,7 +698,8 @@ public class StatusBarService extends IStatusBar.Stub && (oldData == null || oldData.tickerText == null || !CharSequences.equals(oldData.tickerText, n.tickerText))) { - if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) { + if (0 == (mDisabled & + (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText); } } @@ -719,7 +731,7 @@ public class StatusBarService extends IStatusBar.Stub rightIcons[slotIndex] = icon; mStatusIcons.addView(icon.view, pos); } else { - Log.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot); + Slog.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot); mIconMap.remove(key); mIconList.remove(icon); return ; @@ -748,7 +760,7 @@ public class StatusBarService extends IStatusBar.Stub /* private */ void performSetIconVisibility(IBinder key, boolean visible) { synchronized (mIconMap) { if (SPEW) { - Log.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible); + Slog.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible); } StatusBarIcon icon = mIconMap.get(key); icon.view.setVisibility(visible ? View.VISIBLE : View.GONE); @@ -758,7 +770,7 @@ public class StatusBarService extends IStatusBar.Stub /* private */ void performRemoveIcon(IBinder key) { synchronized (this) { if (SPEW) { - Log.d(TAG, "performRemoveIcon key=" + key); + Slog.d(TAG, "performRemoveIcon key=" + key); } StatusBarIcon icon = mIconMap.remove(key); mIconList.remove(icon); @@ -845,7 +857,7 @@ public class StatusBarService extends IStatusBar.Stub exception = e; } if (child == null) { - Log.e(TAG, "couldn't inflate view for package " + n.pkg, exception); + Slog.e(TAG, "couldn't inflate view for package " + n.pkg, exception); return null; } content.addView(child); @@ -882,11 +894,14 @@ public class StatusBarService extends IStatusBar.Stub void updateNotificationView(StatusBarNotification notification, NotificationData oldData) { NotificationData n = notification.data; if (oldData != null && n != null + && n.when == oldData.when + && n.ongoingEvent == oldData.ongoingEvent && n.contentView != null && oldData.contentView != null && n.contentView.getPackage() != null && oldData.contentView.getPackage() != null && oldData.contentView.getPackage().equals(n.contentView.getPackage()) - && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()) { + && oldData.contentView.getLayoutId() == n.contentView.getLayoutId() + && notification.view != null) { mNotificationData.update(notification); try { n.contentView.reapply(mContext, notification.contentView); @@ -901,7 +916,7 @@ public class StatusBarService extends IStatusBar.Stub } catch (RuntimeException e) { // It failed to add cleanly. Log, and remove the view from the panel. - Log.w(TAG, "couldn't reapply views for package " + n.contentView.getPackage(), e); + Slog.w(TAG, "couldn't reapply views for package " + n.contentView.getPackage(), e); removeNotificationView(notification); } } else { @@ -942,7 +957,7 @@ public class StatusBarService extends IStatusBar.Stub } private void makeExpandedVisible() { - if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); + if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); if (mExpandedVisible) { return; } @@ -962,7 +977,7 @@ public class StatusBarService extends IStatusBar.Stub } void animateExpand() { - if (SPEW) Log.d(TAG, "Animate expand: expanded=" + mExpanded); + if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded); if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { return ; } @@ -970,15 +985,17 @@ public class StatusBarService extends IStatusBar.Stub return; } - prepareTracking(0); + prepareTracking(0, true); performFling(0, 2000.0f, true); } void animateCollapse() { if (SPEW) { - Log.d(TAG, "animateCollapse(): mExpanded=" + mExpanded + Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded + " mExpandedVisible=" + mExpandedVisible + + " mExpanded=" + mExpanded + " mAnimating=" + mAnimating + + " mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel); } @@ -986,17 +1003,21 @@ public class StatusBarService extends IStatusBar.Stub return; } + int y; if (mAnimating) { - return; + y = (int)mAnimY; + } else { + y = mDisplay.getHeight()-1; } - - int y = mDisplay.getHeight()-1; - prepareTracking(y); + // Let the fling think that we're open so it goes in the right direction + // and doesn't try to re-open the windowshade. + mExpanded = true; + prepareTracking(y, false); performFling(y, -2000.0f, true); } void performExpand() { - if (SPEW) Log.d(TAG, "performExpand: mExpanded=" + mExpanded); + if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded); if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { return ; } @@ -1021,7 +1042,7 @@ public class StatusBarService extends IStatusBar.Stub } void performCollapse() { - if (SPEW) Log.d(TAG, "performCollapse: mExpanded=" + mExpanded + if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded + " mExpandedVisible=" + mExpandedVisible); if (!mExpandedVisible) { @@ -1047,18 +1068,18 @@ public class StatusBarService extends IStatusBar.Stub void doAnimation() { if (mAnimating) { - if (SPEW) Log.d(TAG, "doAnimation"); - if (SPEW) Log.d(TAG, "doAnimation before mAnimY=" + mAnimY); + if (SPEW) Slog.d(TAG, "doAnimation"); + if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY); incrementAnim(); - if (SPEW) Log.d(TAG, "doAnimation after mAnimY=" + mAnimY); + if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY); if (mAnimY >= mDisplay.getHeight()-1) { - if (SPEW) Log.d(TAG, "Animation completed to expanded state."); + if (SPEW) Slog.d(TAG, "Animation completed to expanded state."); mAnimating = false; updateExpandedViewPos(EXPANDED_FULL_OPEN); performExpand(); } else if (mAnimY < mStatusBarView.getHeight()) { - if (SPEW) Log.d(TAG, "Animation completed to collapsed state."); + if (SPEW) Slog.d(TAG, "Animation completed to collapsed state."); mAnimating = false; updateExpandedViewPos(0); performCollapse(); @@ -1086,7 +1107,7 @@ public class StatusBarService extends IStatusBar.Stub mAnimY = y + (v*t) + (0.5f*a*t*t); // px mAnimVel = v + (a*t); // px/s mAnimLastTime = now; // ms - //Log.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY + //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY // + " mAnimAccel=" + mAnimAccel); } @@ -1106,10 +1127,9 @@ public class StatusBarService extends IStatusBar.Stub } } - void prepareTracking(int y) { + void prepareTracking(int y, boolean opening) { mTracking = true; mVelocityTracker = VelocityTracker.obtain(); - boolean opening = !mExpanded; if (opening) { mAnimAccel = 2000.0f; mAnimVel = 200; @@ -1143,7 +1163,7 @@ public class StatusBarService extends IStatusBar.Stub mAnimY = y; mAnimVel = vel; - //Log.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel); + //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel); if (mExpanded) { if (!always && ( @@ -1183,7 +1203,7 @@ public class StatusBarService extends IStatusBar.Stub } } } - //Log.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel + //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel // + " mAnimAccel=" + mAnimAccel); long now = SystemClock.uptimeMillis(); @@ -1197,16 +1217,19 @@ public class StatusBarService extends IStatusBar.Stub } boolean interceptTouchEvent(MotionEvent event) { - if (SPEW) Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event); + if (SPEW) { + Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled=" + + mDisabled); + } if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { - return true; + return false; } final int statusBarSize = mStatusBarView.getHeight(); final int hitSize = statusBarSize*2; if (event.getAction() == MotionEvent.ACTION_DOWN) { - int y = (int)event.getRawY(); + final int y = (int)event.getRawY(); if (!mExpanded) { mViewDelta = statusBarSize - y; @@ -1216,8 +1239,16 @@ public class StatusBarService extends IStatusBar.Stub } if ((!mExpanded && y < hitSize) || (mExpanded && y > (mDisplay.getHeight()-hitSize))) { - prepareTracking(y); - mVelocityTracker.addMovement(event); + + // We drop events at the edge of the screen to make the windowshade come + // down by accident less, especially when pushing open a device with a keyboard + // that rotates (like g1 and droid) + int x = (int)event.getRawX(); + final int edgeBorder = mEdgeBorder; + if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) { + prepareTracking(y, !mExpanded);// opening if we're not already fully visible + mVelocityTracker.addMovement(event); + } } } else if (mTracking) { mVelocityTracker.addMovement(event); @@ -1288,7 +1319,7 @@ public class StatusBarService extends IStatusBar.Stub mNotificationCallbacks.onNotificationClick(mPkg, mTag, mId); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. Just log the exception message. - Log.w(TAG, "Sending contentIntent failed: " + e); + Slog.w(TAG, "Sending contentIntent failed: " + e); } deactivate(); } @@ -1367,7 +1398,7 @@ public class StatusBarService extends IStatusBar.Stub return; } - synchronized (mQueue) { + synchronized (mQueueLock) { pw.println("Current Status Bar state:"); pw.println(" mExpanded=" + mExpanded + ", mExpandedVisible=" + mExpandedVisible); @@ -1452,19 +1483,19 @@ public class StatusBarService extends IStatusBar.Stub mHandler.post(new Runnable() { public void run() { mStatusBarView.getLocationOnScreen(mAbsPos); - Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + ") " + mStatusBarView.getWidth() + "x" + mStatusBarView.getHeight()); mStatusBarView.debug(); mExpandedView.getLocationOnScreen(mAbsPos); - Log.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + ") " + mExpandedView.getWidth() + "x" + mExpandedView.getHeight()); mExpandedView.debug(); mTrackingView.getLocationOnScreen(mAbsPos); - Log.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + ") " + mTrackingView.getWidth() + "x" + mTrackingView.getHeight()); mTrackingView.debug(); @@ -1479,15 +1510,15 @@ public class StatusBarService extends IStatusBar.Stub Drawable bg; /// ---------- Tracking View -------------- - pixelFormat = PixelFormat.TRANSLUCENT; + pixelFormat = PixelFormat.RGBX_8888; bg = mTrackingView.getBackground(); if (bg != null) { pixelFormat = bg.getOpacity(); } lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS @@ -1496,6 +1527,7 @@ public class StatusBarService extends IStatusBar.Stub // lp.token = mStatusBarView.getWindowToken(); lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.setTitle("TrackingView"); + lp.y = mTrackingPosition; mTrackingParams = lp; WindowManagerImpl.getDefault().addView(mTrackingView, lp); @@ -1508,20 +1540,11 @@ public class StatusBarService extends IStatusBar.Stub /// ---------- Expanded View -------------- pixelFormat = PixelFormat.TRANSLUCENT; - bg = mExpandedView.getBackground(); - if (bg != null) { - pixelFormat = bg.getOpacity(); - if (pixelFormat != PixelFormat.TRANSLUCENT) { - // we want good-looking gradients, so we force a 8-bits per - // pixel format. - pixelFormat = PixelFormat.RGBX_8888; - } - } final int disph = mDisplay.getHeight(); lp = mExpandedDialog.getWindow().getAttributes(); - lp.width = ViewGroup.LayoutParams.FILL_PARENT; - lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; + lp.width = ViewGroup.LayoutParams.MATCH_PARENT; + lp.height = getExpandedHeight(); lp.x = 0; mTrackingPosition = lp.y = -disph; // sufficiently large negative lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; @@ -1539,11 +1562,11 @@ public class StatusBarService extends IStatusBar.Stub mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); mExpandedDialog.setContentView(mExpandedView, - new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); + new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + mExpandedDialog.getWindow().setBackgroundDrawable(null); mExpandedDialog.show(); FrameLayout hack = (FrameLayout)mExpandedView.getParent(); - hack.setForeground(null); } void setDateViewVisibility(boolean visible, int anim) { @@ -1563,7 +1586,7 @@ public class StatusBarService extends IStatusBar.Stub void updateExpandedViewPos(int expandedPosition) { if (SPEW) { - Log.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition + Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition + " mTrackingParams.y=" + mTrackingParams.y + " mTrackingPosition=" + mTrackingPosition); } @@ -1575,8 +1598,11 @@ public class StatusBarService extends IStatusBar.Stub // Maybe the view was resized. if (!mExpandedVisible) { if (mTrackingView != null) { - mTrackingPosition = mTrackingParams.y = -disph; - WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); + mTrackingPosition = -disph; + if (mTrackingParams != null) { + mTrackingParams.y = mTrackingPosition; + WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); + } } if (mExpandedParams != null) { mExpandedParams.y = -disph; @@ -1605,11 +1631,15 @@ public class StatusBarService extends IStatusBar.Stub mTrackingParams.height = disph-h; WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); - mCloseView.getLocationInWindow(mCloseLocation); - if (mExpandedParams != null) { + mCloseView.getLocationInWindow(mPositionTmp); + final int closePos = mPositionTmp[1]; + + mExpandedContents.getLocationInWindow(mPositionTmp); + final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight(); + mExpandedParams.y = pos + mTrackingView.getHeight() - - (mTrackingParams.height-mCloseLocation[1]) - mExpandedView.getHeight(); + - (mTrackingParams.height-closePos) - contentsBottom; int max = h; if (mExpandedParams.y > max) { mExpandedParams.y = max; @@ -1619,30 +1649,33 @@ public class StatusBarService extends IStatusBar.Stub mExpandedParams.y = min; } - /* - Log.d(TAG, "mTrackingPosition=" + mTrackingPosition - + " mTrackingView.height=" + mTrackingView.getHeight() - + " diff=" + (mTrackingPosition + mTrackingView.getHeight()) - + " h=" + h); - */ - panelSlightlyVisible((mTrackingPosition + mTrackingView.getHeight()) > h); + boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h; + if (!visible) { + // if the contents aren't visible, move the expanded view way off screen + // because the window itself extends below the content view. + mExpandedParams.y = -disph; + } + panelSlightlyVisible(visible); mExpandedDialog.getWindow().setAttributes(mExpandedParams); } if (SPEW) { - Log.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition + Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition + " mTrackingParams.y=" + mTrackingParams.y + " mTrackingPosition=" + mTrackingPosition - + " mExpandedParams.y=" + mExpandedParams.y); + + " mExpandedParams.y=" + mExpandedParams.y + + " mExpandedParams.height=" + mExpandedParams.height); } } - void updateAvailableHeight() { + int getExpandedHeight() { + return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight(); + } + + void updateExpandedHeight() { if (mExpandedView != null) { - int disph = mDisplay.getHeight(); - int h = mStatusBarView.getHeight(); - int max = disph - (mCloseView.getHeight() + h); - mExpandedView.setMaxHeight(max); + mExpandedParams.height = getExpandedHeight(); + mExpandedDialog.getWindow().setAttributes(mExpandedParams); } } @@ -1672,15 +1705,13 @@ public class StatusBarService extends IStatusBar.Stub // act accordingly if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { if ((net & StatusBarManager.DISABLE_EXPAND) != 0) { - Log.d(TAG, "DISABLE_EXPAND: yes"); - mAnimating = false; - updateExpandedViewPos(0); - performCollapse(); + Slog.d(TAG, "DISABLE_EXPAND: yes"); + animateCollapse(); } } if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { - Log.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); + Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); if (mTicking) { mNotificationIcons.setVisibility(View.INVISIBLE); mTicker.halt(); @@ -1688,11 +1719,15 @@ public class StatusBarService extends IStatusBar.Stub setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out); } } else { - Log.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); + Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); if (!mExpandedVisible) { setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); } } + } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { + if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { + mTicker.halt(); + } } } @@ -1724,7 +1759,7 @@ public class StatusBarService extends IStatusBar.Stub void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { if (false) { - Log.d(TAG, "updateNetworkName showSpn=" + showSpn + " spn=" + spn + Slog.d(TAG, "updateNetworkName showSpn=" + showSpn + " spn=" + spn + " showPlmn=" + showPlmn + " plmn=" + plmn); } boolean something = false; @@ -1757,11 +1792,16 @@ public class StatusBarService extends IStatusBar.Stub * meantime, just update the things that we know change. */ void updateResources() { + Resources res = mContext.getResources(); + mClearButton.setText(mContext.getText(R.string.status_bar_clear_all_button)); mOngoingTitle.setText(mContext.getText(R.string.status_bar_ongoing_events_title)); mLatestTitle.setText(mContext.getText(R.string.status_bar_latest_events_title)); mNoNotificationsTitle.setText(mContext.getText(R.string.status_bar_no_notifications_title)); - if (false) Log.v(TAG, "updateResources"); + + mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); + + if (false) Slog.v(TAG, "updateResources"); } // @@ -1782,7 +1822,7 @@ public class StatusBarService extends IStatusBar.Stub public void run() { vibrate(); SystemClock.sleep(250); - Log.d(TAG, "startTracing"); + Slog.d(TAG, "startTracing"); android.os.Debug.startMethodTracing("/data/statusbar-traces/trace"); mHandler.postDelayed(mStopTracing, 10000); } @@ -1791,7 +1831,7 @@ public class StatusBarService extends IStatusBar.Stub Runnable mStopTracing = new Runnable() { public void run() { android.os.Debug.stopMethodTracing(); - Log.d(TAG, "stopTracing"); + Slog.d(TAG, "stopTracing"); vibrate(); } }; @@ -1803,16 +1843,30 @@ public class StatusBarService extends IStatusBar.Stub filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addDataScheme("package"); mContext.registerReceiver(this, filter); + IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + mContext.registerReceiver(this, sdFilter); } @Override public void onReceive(Context context, Intent intent) { - ArrayList<StatusBarNotification> list = null; - synchronized (StatusBarService.this) { + String pkgList[] = null; + if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + } else { Uri data = intent.getData(); if (data != null) { String pkg = data.getSchemeSpecificPart(); - list = mNotificationData.notificationsForPackage(pkg); + if (pkg != null) { + pkgList = new String[]{pkg}; + } + } + } + ArrayList<StatusBarNotification> list = null; + if (pkgList != null) { + synchronized (StatusBarService.this) { + for (String pkg : pkgList) { + list = mNotificationData.notificationsForPackage(pkg); + } } } diff --git a/services/java/com/android/server/status/StatusBarView.java b/services/java/com/android/server/status/StatusBarView.java index 35dfb81..5e1f572 100644 --- a/services/java/com/android/server/status/StatusBarView.java +++ b/services/java/com/android/server/status/StatusBarView.java @@ -1,9 +1,26 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Canvas; +import android.os.SystemClock; import android.util.AttributeSet; -import android.util.Log; -import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -15,6 +32,8 @@ import com.android.internal.R; public class StatusBarView extends FrameLayout { private static final String TAG = "StatusBarView"; + static final int DIM_ANIM_TIME = 400; + StatusBarService mService; boolean mTracking; int mStartX, mStartY; @@ -22,6 +41,10 @@ public class StatusBarView extends FrameLayout { ViewGroup mStatusIcons; View mDate; FixedSizeDrawable mBackground; + + boolean mNightMode = false; + int mStartAlpha = 0, mEndAlpha = 0; + long mEndTime = 0; public StatusBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -44,7 +67,30 @@ public class StatusBarView extends FrameLayout { super.onAttachedToWindow(); mService.onBarViewAttached(); } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) + == Configuration.UI_MODE_NIGHT_YES; + if (mNightMode != nightMode) { + mNightMode = nightMode; + mStartAlpha = getCurAlpha(); + mEndAlpha = mNightMode ? 0x80 : 0x00; + mEndTime = SystemClock.uptimeMillis() + DIM_ANIM_TIME; + invalidate(); + } + } + int getCurAlpha() { + long time = SystemClock.uptimeMillis(); + if (time > mEndTime) { + return mEndAlpha; + } + return mEndAlpha + - (int)(((mEndAlpha-mStartAlpha) * (mEndTime-time) / DIM_ANIM_TIME)); + } + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); @@ -81,6 +127,18 @@ public class StatusBarView extends FrameLayout { mBackground.setFixedBounds(-mDate.getLeft(), -mDate.getTop(), (r-l), (b-t)); } + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + int alpha = getCurAlpha(); + if (alpha != 0) { + canvas.drawARGB(alpha, 0, 0, 0); + } + if (alpha != mEndAlpha) { + invalidate(); + } + } + /** * Gets the left position of v in this view. Throws if v is not * a child of this. diff --git a/services/java/com/android/server/status/StorageNotification.java b/services/java/com/android/server/status/StorageNotification.java new file mode 100644 index 0000000..8da8cd3 --- /dev/null +++ b/services/java/com/android/server/status/StorageNotification.java @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.status; + +import android.app.Activity; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.storage.IMountService; +import android.os.Message; +import android.os.ServiceManager; +import android.os.storage.StorageEventListener; +import android.os.storage.StorageManager; +import android.os.storage.StorageResultCode; +import android.provider.Settings; +import android.util.Slog; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +public class StorageNotification extends StorageEventListener { + private static final String TAG = "StorageNotification"; + + private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true; + + /** + * Binder context for this service + */ + private Context mContext; + + /** + * The notification that is shown when a USB mass storage host + * is connected. + * <p> + * This is lazily created, so use {@link #setUsbStorageNotification()}. + */ + private Notification mUsbStorageNotification; + + /** + * The notification that is shown when the following media events occur: + * - Media is being checked + * - Media is blank (or unknown filesystem) + * - Media is corrupt + * - Media is safe to unmount + * - Media is missing + * <p> + * This is lazily created, so use {@link #setMediaStorageNotification()}. + */ + private Notification mMediaStorageNotification; + private boolean mUmsAvailable; + private StorageManager mStorageManager; + + public StorageNotification(Context context) { + mContext = context; + + mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); + mUmsAvailable = mStorageManager.isUsbMassStorageConnected(); + Slog.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", mUmsAvailable, + Environment.getExternalStorageState())); + } + + /* + * @override com.android.os.storage.StorageEventListener + */ + @Override + public void onUsbMassStorageConnectionChanged(boolean connected) { + mUmsAvailable = connected; + /* + * Even though we may have a UMS host connected, we the SD card + * may not be in a state for export. + */ + String st = Environment.getExternalStorageState(); + + Slog.i(TAG, String.format("UMS connection changed to %s (media state %s)", connected, st)); + + if (connected && (st.equals( + Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) { + /* + * No card or card being checked = don't display + */ + connected = false; + } + updateUsbMassStorageNotification(connected); + } + + /* + * @override com.android.os.storage.StorageEventListener + */ + @Override + public void onStorageStateChanged(String path, String oldState, String newState) { + Slog.i(TAG, String.format( + "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState)); + if (newState.equals(Environment.MEDIA_SHARED)) { + /* + * Storage is now shared. Modify the UMS notification + * for stopping UMS. + */ + Intent intent = new Intent(); + intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + setUsbStorageNotification( + com.android.internal.R.string.usb_storage_stop_notification_title, + com.android.internal.R.string.usb_storage_stop_notification_message, + com.android.internal.R.drawable.stat_sys_warning, false, true, pi); + } else if (newState.equals(Environment.MEDIA_CHECKING)) { + /* + * Storage is now checking. Update media notification and disable + * UMS notification. + */ + setMediaStorageNotification( + com.android.internal.R.string.ext_media_checking_notification_title, + com.android.internal.R.string.ext_media_checking_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null); + updateUsbMassStorageNotification(false); + } else if (newState.equals(Environment.MEDIA_MOUNTED)) { + /* + * Storage is now mounted. Dismiss any media notifications, + * and enable UMS notification if connected. + */ + setMediaStorageNotification(0, 0, 0, false, false, null); + updateUsbMassStorageNotification(mUmsAvailable); + } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) { + /* + * Storage is now unmounted. We may have been unmounted + * because the user is enabling/disabling UMS, in which case we don't + * want to display the 'safe to unmount' notification. + */ + if (!mStorageManager.isUsbMassStorageEnabled()) { + if (oldState.equals(Environment.MEDIA_SHARED)) { + /* + * The unmount was due to UMS being enabled. Dismiss any + * media notifications, and enable UMS notification if connected + */ + setMediaStorageNotification(0, 0, 0, false, false, null); + updateUsbMassStorageNotification(mUmsAvailable); + } else { + /* + * Show safe to unmount media notification, and enable UMS + * notification if connected. + */ + setMediaStorageNotification( + com.android.internal.R.string.ext_media_safe_unmount_notification_title, + com.android.internal.R.string.ext_media_safe_unmount_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard, true, true, null); + updateUsbMassStorageNotification(mUmsAvailable); + } + } else { + /* + * The unmount was due to UMS being enabled. Dismiss any + * media notifications, and disable the UMS notification + */ + setMediaStorageNotification(0, 0, 0, false, false, null); + updateUsbMassStorageNotification(false); + } + } else if (newState.equals(Environment.MEDIA_NOFS)) { + /* + * Storage has no filesystem. Show blank media notification, + * and enable UMS notification if connected. + */ + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + setMediaStorageNotification( + com.android.internal.R.string.ext_media_nofs_notification_title, + com.android.internal.R.string.ext_media_nofs_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi); + updateUsbMassStorageNotification(mUmsAvailable); + } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) { + /* + * Storage is corrupt. Show corrupt media notification, + * and enable UMS notification if connected. + */ + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + setMediaStorageNotification( + com.android.internal.R.string.ext_media_unmountable_notification_title, + com.android.internal.R.string.ext_media_unmountable_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi); + updateUsbMassStorageNotification(mUmsAvailable); + } else if (newState.equals(Environment.MEDIA_REMOVED)) { + /* + * Storage has been removed. Show nomedia media notification, + * and disable UMS notification regardless of connection state. + */ + setMediaStorageNotification( + com.android.internal.R.string.ext_media_nomedia_notification_title, + com.android.internal.R.string.ext_media_nomedia_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard_usb, + true, false, null); + updateUsbMassStorageNotification(false); + } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) { + /* + * Storage has been removed unsafely. Show bad removal media notification, + * and disable UMS notification regardless of connection state. + */ + setMediaStorageNotification( + com.android.internal.R.string.ext_media_badremoval_notification_title, + com.android.internal.R.string.ext_media_badremoval_notification_message, + com.android.internal.R.drawable.stat_sys_warning, + true, true, null); + updateUsbMassStorageNotification(false); + } else { + Slog.w(TAG, String.format("Ignoring unknown state {%s}", newState)); + } + } + + /** + * Update the state of the USB mass storage notification + */ + void updateUsbMassStorageNotification(boolean available) { + + if (available) { + Intent intent = new Intent(); + intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + final boolean adbOn = 1 == Settings.Secure.getInt( + mContext.getContentResolver(), + Settings.Secure.ADB_ENABLED, + 0); + + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + setUsbStorageNotification( + com.android.internal.R.string.usb_storage_notification_title, + com.android.internal.R.string.usb_storage_notification_message, + com.android.internal.R.drawable.stat_sys_data_usb, + false, true, pi); + + if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) { + // We assume that developers don't want to enable UMS every + // time they attach a device to a USB host. The average user, + // however, is looking to charge the phone (in which case this + // is harmless) or transfer files (in which case this coaches + // the user about how to complete that task and saves several + // steps). + mContext.startActivity(intent); + } + } else { + setUsbStorageNotification(0, 0, 0, false, false, null); + } + } + + /** + * Sets the USB storage notification. + */ + private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, + boolean sound, boolean visible, PendingIntent pi) { + + if (!visible && mUsbStorageNotification == null) { + return; + } + + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + + if (notificationManager == null) { + return; + } + + if (visible) { + Resources r = Resources.getSystem(); + CharSequence title = r.getText(titleId); + CharSequence message = r.getText(messageId); + + if (mUsbStorageNotification == null) { + mUsbStorageNotification = new Notification(); + mUsbStorageNotification.icon = icon; + mUsbStorageNotification.when = 0; + } + + if (sound) { + mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; + } else { + mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; + } + + mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; + + mUsbStorageNotification.tickerText = title; + if (pi == null) { + Intent intent = new Intent(); + pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + } + + mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); + } + + final int notificationId = mUsbStorageNotification.icon; + if (visible) { + notificationManager.notify(notificationId, mUsbStorageNotification); + } else { + notificationManager.cancel(notificationId); + } + } + + private synchronized boolean getMediaStorageNotificationDismissable() { + if ((mMediaStorageNotification != null) && + ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) == + Notification.FLAG_AUTO_CANCEL)) + return true; + + return false; + } + + /** + * Sets the media storage notification. + */ + private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, + boolean dismissable, PendingIntent pi) { + + if (!visible && mMediaStorageNotification == null) { + return; + } + + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + + if (notificationManager == null) { + return; + } + + if (mMediaStorageNotification != null && visible) { + /* + * Dismiss the previous notification - we're about to + * re-use it. + */ + final int notificationId = mMediaStorageNotification.icon; + notificationManager.cancel(notificationId); + } + + if (visible) { + Resources r = Resources.getSystem(); + CharSequence title = r.getText(titleId); + CharSequence message = r.getText(messageId); + + if (mMediaStorageNotification == null) { + mMediaStorageNotification = new Notification(); + mMediaStorageNotification.when = 0; + } + + mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; + + if (dismissable) { + mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; + } else { + mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; + } + + mMediaStorageNotification.tickerText = title; + if (pi == null) { + Intent intent = new Intent(); + pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + } + + mMediaStorageNotification.icon = icon; + mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi); + } + + final int notificationId = mMediaStorageNotification.icon; + if (visible) { + notificationManager.notify(notificationId, mMediaStorageNotification); + } else { + notificationManager.cancel(notificationId); + } + } +} diff --git a/services/java/com/android/server/status/Ticker.java b/services/java/com/android/server/status/Ticker.java index c93ee0d..e47185b 100644 --- a/services/java/com/android/server/status/Ticker.java +++ b/services/java/com/android/server/status/Ticker.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import com.android.internal.R; @@ -9,7 +25,7 @@ import android.text.StaticLayout; import android.text.Layout.Alignment; import android.text.TextPaint; import android.text.TextUtils; -import android.util.Log; +import android.util.Slog; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; diff --git a/services/java/com/android/server/status/TickerView.java b/services/java/com/android/server/status/TickerView.java index 349c7f4..099dffb 100644 --- a/services/java/com/android/server/status/TickerView.java +++ b/services/java/com/android/server/status/TickerView.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.server.status; diff --git a/services/java/com/android/server/status/TrackingPatternView.java b/services/java/com/android/server/status/TrackingPatternView.java index 4cb8eff..2c91aa4 100644 --- a/services/java/com/android/server/status/TrackingPatternView.java +++ b/services/java/com/android/server/status/TrackingPatternView.java @@ -19,7 +19,7 @@ package com.android.server.status; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; -import android.util.Log; +import android.util.Slog; import android.view.View; import android.graphics.BitmapFactory; import android.graphics.Bitmap; diff --git a/services/java/com/android/server/status/TrackingView.java b/services/java/com/android/server/status/TrackingView.java index 722d10c..8ec39c0 100644 --- a/services/java/com/android/server/status/TrackingView.java +++ b/services/java/com/android/server/status/TrackingView.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.status; import android.content.Context; @@ -23,7 +39,7 @@ public class TrackingView extends LinearLayout { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - mService.updateAvailableHeight(); + mService.updateExpandedHeight(); } @Override diff --git a/services/java/com/android/server/status/UsbStorageActivity.java b/services/java/com/android/server/status/UsbStorageActivity.java new file mode 100644 index 0000000..e8631c5 --- /dev/null +++ b/services/java/com/android/server/status/UsbStorageActivity.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.status; + +import com.android.internal.R; +import android.app.Activity; +import android.app.ActivityManager; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.DialogInterface.OnCancelListener; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.IBinder; +import android.os.storage.IMountService; +import android.os.storage.StorageManager; +import android.os.storage.StorageEventListener; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.widget.ImageView; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.view.View; +import android.view.Window; +import android.util.Log; + +import java.util.List; + +/** + * This activity is shown to the user for him/her to enable USB mass storage + * on-demand (that is, when the USB cable is connected). It uses the alert + * dialog style. It will be launched from a notification. + */ +public class UsbStorageActivity extends Activity + implements View.OnClickListener, OnCancelListener { + private static final String TAG = "UsbStorageActivity"; + + private Button mMountButton; + private Button mUnmountButton; + private ProgressBar mProgressBar; + private TextView mBanner; + private TextView mMessage; + private ImageView mIcon; + private StorageManager mStorageManager = null; + private static final int DLG_CONFIRM_KILL_STORAGE_USERS = 1; + private static final int DLG_ERROR_SHARING = 2; + static final boolean localLOGV = false; + + /** Used to detect when the USB cable is unplugged, so we can call finish() */ + private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) { + handleBatteryChanged(intent); + } + } + }; + + private StorageEventListener mStorageListener = new StorageEventListener() { + @Override + public void onStorageStateChanged(String path, String oldState, String newState) { + final boolean on = newState.equals(Environment.MEDIA_SHARED); + switchDisplay(on); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (mStorageManager == null) { + mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE); + if (mStorageManager == null) { + Log.w(TAG, "Failed to get StorageManager"); + } + } + + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setProgressBarIndeterminateVisibility(true); + + setTitle(getString(com.android.internal.R.string.usb_storage_activity_title)); + + setContentView(com.android.internal.R.layout.usb_storage_activity); + + mIcon = (ImageView) findViewById(com.android.internal.R.id.icon); + mBanner = (TextView) findViewById(com.android.internal.R.id.banner); + mMessage = (TextView) findViewById(com.android.internal.R.id.message); + + mMountButton = (Button) findViewById(com.android.internal.R.id.mount_button); + mMountButton.setOnClickListener(this); + mUnmountButton = (Button) findViewById(com.android.internal.R.id.unmount_button); + mUnmountButton.setOnClickListener(this); + mProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress); + } + + private void switchDisplay(boolean usbStorageInUse) { + if (usbStorageInUse) { + mProgressBar.setVisibility(View.GONE); + mUnmountButton.setVisibility(View.VISIBLE); + mMountButton.setVisibility(View.GONE); + mIcon.setImageResource(com.android.internal.R.drawable.usb_android_connected); + mBanner.setText(com.android.internal.R.string.usb_storage_stop_title); + mMessage.setText(com.android.internal.R.string.usb_storage_stop_message); + } else { + mProgressBar.setVisibility(View.GONE); + mUnmountButton.setVisibility(View.GONE); + mMountButton.setVisibility(View.VISIBLE); + mIcon.setImageResource(com.android.internal.R.drawable.usb_android); + mBanner.setText(com.android.internal.R.string.usb_storage_title); + mMessage.setText(com.android.internal.R.string.usb_storage_message); + } + } + + @Override + protected void onResume() { + super.onResume(); + + mStorageManager.registerListener(mStorageListener); + registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + try { + switchDisplay(mStorageManager.isUsbMassStorageEnabled()); + } catch (Exception ex) { + Log.e(TAG, "Failed to read UMS enable state", ex); + } + } + + @Override + protected void onPause() { + super.onPause(); + + unregisterReceiver(mBatteryReceiver); + if (mStorageManager == null && mStorageListener != null) { + mStorageManager.unregisterListener(mStorageListener); + } + } + + private void handleBatteryChanged(Intent intent) { + int pluggedType = intent.getIntExtra("plugged", 0); + if (pluggedType == 0) { + // It was disconnected from the plug, so finish + finish(); + } + } + + private IMountService getMountService() { + IBinder service = ServiceManager.getService("mount"); + if (service != null) { + return IMountService.Stub.asInterface(service); + } + return null; + } + + @Override + public Dialog onCreateDialog(int id, Bundle args) { + switch (id) { + case DLG_CONFIRM_KILL_STORAGE_USERS: + return new AlertDialog.Builder(this) + .setTitle(R.string.dlg_confirm_kill_storage_users_title) + .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + switchUsbMassStorageAsync(true); + }}) + .setNegativeButton(R.string.cancel, null) + .setMessage(R.string.dlg_confirm_kill_storage_users_text) + .setOnCancelListener(this) + .create(); + case DLG_ERROR_SHARING: + return new AlertDialog.Builder(this) + .setTitle(R.string.dlg_error_title) + .setNeutralButton(R.string.dlg_ok, null) + .setMessage(R.string.usb_storage_error_message) + .setOnCancelListener(this) + .create(); + } + return null; + } + + private void showDialogInner(int id) { + removeDialog(id); + showDialog(id); + } + + private void switchUsbMassStorageAsync(boolean on) { + mUnmountButton.setVisibility(View.GONE); + mMountButton.setVisibility(View.GONE); + + mProgressBar.setVisibility(View.VISIBLE); + // will be hidden once USB mass storage kicks in (or fails) + + final boolean _on = on; + new Thread() { + public void run() { + if (_on) { + mStorageManager.enableUsbMassStorage(); + } else { + mStorageManager.disableUsbMassStorage(); + } + } + }.start(); + } + + private void checkStorageUsers() { + IMountService ims = getMountService(); + if (ims == null) { + // Display error dialog + showDialogInner(DLG_ERROR_SHARING); + } + String extStoragePath = Environment.getExternalStorageDirectory().toString(); + boolean showDialog = false; + try { + int[] stUsers = ims.getStorageUsers(extStoragePath); + if (stUsers != null && stUsers.length > 0) { + showDialog = true; + } else { + // List of applications on sdcard. + ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); + List<ApplicationInfo> infoList = am.getRunningExternalApplications(); + if (infoList != null && infoList.size() > 0) { + showDialog = true; + } + } + } catch (RemoteException e) { + // Display error dialog + showDialogInner(DLG_ERROR_SHARING); + } + if (showDialog) { + // Display dialog to user + showDialogInner(DLG_CONFIRM_KILL_STORAGE_USERS); + } else { + if (localLOGV) Log.i(TAG, "Enabling UMS"); + switchUsbMassStorageAsync(true); + } + } + + public void onClick(View v) { + if (v == mMountButton) { + // Check for list of storage users and display dialog if needed. + checkStorageUsers(); + } else if (v == mUnmountButton) { + if (localLOGV) Log.i(TAG, "Disabling UMS"); + switchUsbMassStorageAsync(false); + } + } + + public void onCancel(DialogInterface dialog) { + finish(); + } + +} diff --git a/services/jni/Android.mk b/services/jni/Android.mk index 2f48edf..9d2760e 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -4,10 +4,11 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ com_android_server_AlarmManagerService.cpp \ com_android_server_BatteryService.cpp \ - com_android_server_HardwareService.cpp \ com_android_server_KeyInputQueue.cpp \ + com_android_server_LightsService.cpp \ com_android_server_SensorService.cpp \ com_android_server_SystemServer.cpp \ + com_android_server_VibratorService.cpp \ onload.cpp LOCAL_C_INCLUDES += \ diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp index 85d63c9..0e162bd 100644 --- a/services/jni/com_android_server_AlarmManagerService.cpp +++ b/services/jni/com_android_server_AlarmManagerService.cpp @@ -38,10 +38,6 @@ #include <linux/android_alarm.h> #endif -#define ONE_NANOSECOND 1000000000LL -#define NANOSECONDS_TO_SECONDS(x) (x / ONE_NANOSECOND) -#define SECONDS_TO_NANOSECONDS(x) (x * ONE_NANOSECOND) - namespace android { static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv* env, jobject obj, jint fd, jint minswest) @@ -82,17 +78,17 @@ static void android_server_AlarmManagerService_close(JNIEnv* env, jobject obj, j #endif } -static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong nanoseconds) +static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds) { #if HAVE_ANDROID_OS struct timespec ts; - ts.tv_sec = NANOSECONDS_TO_SECONDS(nanoseconds); - ts.tv_nsec = nanoseconds - SECONDS_TO_NANOSECONDS(ts.tv_sec); + ts.tv_sec = seconds; + ts.tv_nsec = nanoseconds; int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts); if (result < 0) { - LOGE("Unable to set alarm to %lld: %s\n", nanoseconds, strerror(errno)); + LOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno)); } #endif } @@ -121,7 +117,7 @@ static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"init", "()I", (void*)android_server_AlarmManagerService_init}, {"close", "(I)V", (void*)android_server_AlarmManagerService_close}, - {"set", "(IIJ)V", (void*)android_server_AlarmManagerService_set}, + {"set", "(IIJJ)V", (void*)android_server_AlarmManagerService_set}, {"waitForAlarm", "(I)I", (void*)android_server_AlarmManagerService_waitForAlarm}, {"setKernelTimezone", "(II)I", (void*)android_server_AlarmManagerService_setKernelTimezone}, }; diff --git a/services/jni/com_android_server_HardwareService.cpp b/services/jni/com_android_server_LightsService.cpp index 253e655..9ed4951 100644 --- a/services/jni/com_android_server_HardwareService.cpp +++ b/services/jni/com_android_server_LightsService.cpp @@ -1,21 +1,20 @@ -/* //device/libs/android_runtime/android_os_Vibrator.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "HardwareService" +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "LightsService" #include "jni.h" #include "JNIHelp.h" @@ -23,18 +22,16 @@ #include <utils/misc.h> #include <utils/Log.h> -#include <hardware_legacy/vibrator.h> #include <hardware/hardware.h> #include <hardware/lights.h> #include <stdio.h> -//#include <string.h> namespace android { // These values must correspond with the LIGHT_ID constants in -// HardwareService.java +// LightsService.java enum { LIGHT_INDEX_BACKLIGHT = 0, LIGHT_INDEX_KEYBOARD = 1, @@ -42,6 +39,8 @@ enum { LIGHT_INDEX_BATTERY = 3, LIGHT_INDEX_NOTIFICATIONS = 4, LIGHT_INDEX_ATTENTION = 5, + LIGHT_INDEX_BLUETOOTH = 6, + LIGHT_INDEX_WIFI = 7, LIGHT_COUNT }; @@ -83,6 +82,10 @@ static jint init_native(JNIEnv *env, jobject clazz) = get_device(module, LIGHT_ID_NOTIFICATIONS); devices->lights[LIGHT_INDEX_ATTENTION] = get_device(module, LIGHT_ID_ATTENTION); + devices->lights[LIGHT_INDEX_BLUETOOTH] + = get_device(module, LIGHT_ID_BLUETOOTH); + devices->lights[LIGHT_INDEX_WIFI] + = get_device(module, LIGHT_ID_WIFI); } else { memset(devices, 0, sizeof(Devices)); } @@ -120,29 +123,15 @@ static void setLight_native(JNIEnv *env, jobject clazz, int ptr, devices->lights[light]->set_light(devices->lights[light], &state); } -static void vibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms) -{ - // LOGI("vibratorOn\n"); - vibrator_on(timeout_ms); -} - -static void vibratorOff(JNIEnv *env, jobject clazz) -{ - // LOGI("vibratorOff\n"); - vibrator_off(); -} - static JNINativeMethod method_table[] = { { "init_native", "()I", (void*)init_native }, { "finalize_native", "(I)V", (void*)finalize_native }, { "setLight_native", "(IIIIIII)V", (void*)setLight_native }, - { "vibratorOn", "(J)V", (void*)vibratorOn }, - { "vibratorOff", "()V", (void*)vibratorOff } }; -int register_android_server_HardwareService(JNIEnv *env) +int register_android_server_LightsService(JNIEnv *env) { - return jniRegisterNativeMethods(env, "com/android/server/HardwareService", + return jniRegisterNativeMethods(env, "com/android/server/LightsService", method_table, NELEM(method_table)); } diff --git a/services/jni/com_android_server_SensorService.cpp b/services/jni/com_android_server_SensorService.cpp index 3911d1f..77db6da 100644 --- a/services/jni/com_android_server_SensorService.cpp +++ b/services/jni/com_android_server_SensorService.cpp @@ -16,7 +16,6 @@ #define LOG_TAG "SensorService" -#define LOG_NDEBUG 0 #include "utils/Log.h" #include <hardware/sensors.h> diff --git a/services/jni/com_android_server_VibratorService.cpp b/services/jni/com_android_server_VibratorService.cpp new file mode 100644 index 0000000..6ec5c07 --- /dev/null +++ b/services/jni/com_android_server_VibratorService.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorService" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <utils/misc.h> +#include <utils/Log.h> +#include <hardware_legacy/vibrator.h> + +#include <stdio.h> + +namespace android +{ + +static void vibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms) +{ + // LOGI("vibratorOn\n"); + vibrator_on(timeout_ms); +} + +static void vibratorOff(JNIEnv *env, jobject clazz) +{ + // LOGI("vibratorOff\n"); + vibrator_off(); +} + +static JNINativeMethod method_table[] = { + { "vibratorOn", "(J)V", (void*)vibratorOn }, + { "vibratorOff", "()V", (void*)vibratorOff } +}; + +int register_android_server_VibratorService(JNIEnv *env) +{ + return jniRegisterNativeMethods(env, "com/android/server/VibratorService", + method_table, NELEM(method_table)); +} + +}; diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp index 26200d3..c16fdb8 100644 --- a/services/jni/onload.cpp +++ b/services/jni/onload.cpp @@ -7,8 +7,9 @@ namespace android { int register_android_server_AlarmManagerService(JNIEnv* env); int register_android_server_BatteryService(JNIEnv* env); int register_android_server_KeyInputQueue(JNIEnv* env); -int register_android_server_HardwareService(JNIEnv* env); +int register_android_server_LightsService(JNIEnv* env); int register_android_server_SensorService(JNIEnv* env); +int register_android_server_VibratorService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); }; @@ -26,10 +27,11 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) LOG_ASSERT(env, "Could not retrieve the env!"); register_android_server_KeyInputQueue(env); - register_android_server_HardwareService(env); + register_android_server_LightsService(env); register_android_server_AlarmManagerService(env); register_android_server_BatteryService(env); register_android_server_SensorService(env); + register_android_server_VibratorService(env); register_android_server_SystemServer(env); return JNI_VERSION_1_4; diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk new file mode 100644 index 0000000..b07a10b --- /dev/null +++ b/services/tests/servicestests/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +# Include all test java files. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + + +LOCAL_JAVA_LIBRARIES := android.test.runner services +LOCAL_PACKAGE_NAME := FrameworksServicesTests + +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) + diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml new file mode 100644 index 0000000..5ce109f --- /dev/null +++ b/services/tests/servicestests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.servicestests"> + + <uses-permission android:name="android.permission.READ_LOGS" /> + <uses-permission android:name="android.permission.WRITE_SETTINGS" /> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.frameworks.servicestests" + android:label="Frameworks Services Tests" /> +</manifest> diff --git a/services/tests/servicestests/src/com/android/server/DropBoxTest.java b/services/tests/servicestests/src/com/android/server/DropBoxTest.java new file mode 100644 index 0000000..78a90fb --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/DropBoxTest.java @@ -0,0 +1,567 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.os.DropBoxManager; +import android.os.ServiceManager; +import android.os.StatFs; +import android.provider.Settings; +import android.test.AndroidTestCase; + +import com.android.server.DropBoxManagerService; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.InputStream; +import java.util.Random; +import java.util.zip.GZIPOutputStream; + +/** Test {@link DropBoxManager} functionality. */ +public class DropBoxTest extends AndroidTestCase { + public void tearDown() throws Exception { + ContentResolver cr = getContext().getContentResolver(); + Settings.Secure.putString(cr, Settings.Secure.DROPBOX_AGE_SECONDS, ""); + Settings.Secure.putString(cr, Settings.Secure.DROPBOX_MAX_FILES, ""); + Settings.Secure.putString(cr, Settings.Secure.DROPBOX_QUOTA_KB, ""); + Settings.Secure.putString(cr, Settings.Secure.DROPBOX_TAG_PREFIX + "DropBoxTest", ""); + } + + public void testAddText() throws Exception { + DropBoxManager dropbox = (DropBoxManager) getContext().getSystemService( + Context.DROPBOX_SERVICE); + long before = System.currentTimeMillis(); + Thread.sleep(5); + dropbox.addText("DropBoxTest", "TEST0"); + Thread.sleep(5); + long between = System.currentTimeMillis(); + Thread.sleep(5); + dropbox.addText("DropBoxTest", "TEST1"); + dropbox.addText("DropBoxTest", "TEST2"); + Thread.sleep(5); + long after = System.currentTimeMillis(); + + DropBoxManager.Entry e0 = dropbox.getNextEntry("DropBoxTest", before); + DropBoxManager.Entry e1 = dropbox.getNextEntry("DropBoxTest", e0.getTimeMillis()); + DropBoxManager.Entry e2 = dropbox.getNextEntry("DropBoxTest", e1.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry("DropBoxTest", e2.getTimeMillis())); + + assertTrue(e0.getTimeMillis() > before); + assertTrue(e0.getTimeMillis() < between); + assertTrue(e1.getTimeMillis() > between); + assertTrue(e1.getTimeMillis() < e2.getTimeMillis()); + assertTrue(e2.getTimeMillis() < after); + + assertEquals("TEST0", e0.getText(80)); + assertEquals("TEST1", e1.getText(80)); + assertEquals("TES", e2.getText(3)); + + e0.close(); + e1.close(); + e2.close(); + } + + public void testAddData() throws Exception { + DropBoxManager dropbox = (DropBoxManager) getContext().getSystemService( + Context.DROPBOX_SERVICE); + long before = System.currentTimeMillis(); + dropbox.addData("DropBoxTest", "TEST".getBytes(), 0); + long after = System.currentTimeMillis(); + + DropBoxManager.Entry e = dropbox.getNextEntry("DropBoxTest", before); + assertTrue(null == dropbox.getNextEntry("DropBoxTest", e.getTimeMillis())); + + assertEquals("DropBoxTest", e.getTag()); + assertTrue(e.getTimeMillis() >= before); + assertEquals(0, e.getFlags()); + assertTrue(null == e.getText(80)); + + byte[] buf = new byte[80]; + assertEquals("TEST", new String(buf, 0, e.getInputStream().read(buf))); + + e.close(); + } + + public void testAddFile() throws Exception { + File dir = getEmptyDir("testAddFile"); + long before = System.currentTimeMillis(); + + File f0 = new File(dir, "f0.txt"); + File f1 = new File(dir, "f1.txt.gz"); + File f2 = new File(dir, "f2.dat"); + File f3 = new File(dir, "f2.dat.gz"); + + FileWriter w0 = new FileWriter(f0); + GZIPOutputStream gz1 = new GZIPOutputStream(new FileOutputStream(f1)); + FileOutputStream os2 = new FileOutputStream(f2); + GZIPOutputStream gz3 = new GZIPOutputStream(new FileOutputStream(f3)); + + w0.write("FILE0"); + gz1.write("FILE1".getBytes()); + os2.write("DATA2".getBytes()); + gz3.write("DATA3".getBytes()); + + w0.close(); + gz1.close(); + os2.close(); + gz3.close(); + + DropBoxManager dropbox = (DropBoxManager) getContext().getSystemService( + Context.DROPBOX_SERVICE); + + dropbox.addFile("DropBoxTest", f0, DropBoxManager.IS_TEXT); + dropbox.addFile("DropBoxTest", f1, DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED); + dropbox.addFile("DropBoxTest", f2, 0); + dropbox.addFile("DropBoxTest", f3, DropBoxManager.IS_GZIPPED); + + DropBoxManager.Entry e0 = dropbox.getNextEntry("DropBoxTest", before); + DropBoxManager.Entry e1 = dropbox.getNextEntry("DropBoxTest", e0.getTimeMillis()); + DropBoxManager.Entry e2 = dropbox.getNextEntry("DropBoxTest", e1.getTimeMillis()); + DropBoxManager.Entry e3 = dropbox.getNextEntry("DropBoxTest", e2.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry("DropBoxTest", e3.getTimeMillis())); + + assertTrue(e0.getTimeMillis() > before); + assertTrue(e1.getTimeMillis() > e0.getTimeMillis()); + assertTrue(e2.getTimeMillis() > e1.getTimeMillis()); + assertTrue(e3.getTimeMillis() > e2.getTimeMillis()); + + assertEquals(DropBoxManager.IS_TEXT, e0.getFlags()); + assertEquals(DropBoxManager.IS_TEXT, e1.getFlags()); + assertEquals(0, e2.getFlags()); + assertEquals(0, e3.getFlags()); + + assertEquals("FILE0", e0.getText(80)); + + byte[] buf1 = new byte[80]; + assertEquals("FILE1", new String(buf1, 0, e1.getInputStream().read(buf1))); + + assertTrue(null == e2.getText(80)); + byte[] buf2 = new byte[80]; + assertEquals("DATA2", new String(buf2, 0, e2.getInputStream().read(buf2))); + + assertTrue(null == e3.getText(80)); + byte[] buf3 = new byte[80]; + assertEquals("DATA3", new String(buf3, 0, e3.getInputStream().read(buf3))); + + e0.close(); + e1.close(); + e2.close(); + e3.close(); + } + + public void testAddEntriesInTheFuture() throws Exception { + File dir = getEmptyDir("testAddEntriesInTheFuture"); + long before = System.currentTimeMillis(); + + // Near future: should be allowed to persist + FileWriter w0 = new FileWriter(new File(dir, "DropBoxTest@" + (before + 5000) + ".txt")); + w0.write("FUTURE0"); + w0.close(); + + // Far future: should be collapsed + FileWriter w1 = new FileWriter(new File(dir, "DropBoxTest@" + (before + 100000) + ".txt")); + w1.write("FUTURE1"); + w1.close(); + + // Another far future item, this one gzipped + File f2 = new File(dir, "DropBoxTest@" + (before + 100001) + ".txt.gz"); + GZIPOutputStream gz2 = new GZIPOutputStream(new FileOutputStream(f2)); + gz2.write("FUTURE2".getBytes()); + gz2.close(); + + // Tombstone in the far future + new FileOutputStream(new File(dir, "DropBoxTest@" + (before + 100002) + ".lost")).close(); + + DropBoxManagerService service = new DropBoxManagerService(getContext(), dir); + DropBoxManager dropbox = new DropBoxManager(service); + + // Until a write, the timestamps are taken at face value + DropBoxManager.Entry e0 = dropbox.getNextEntry(null, before); + DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis()); + DropBoxManager.Entry e2 = dropbox.getNextEntry(null, e1.getTimeMillis()); + DropBoxManager.Entry e3 = dropbox.getNextEntry(null, e2.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry(null, e3.getTimeMillis())); + + assertEquals("FUTURE0", e0.getText(80)); + assertEquals("FUTURE1", e1.getText(80)); + assertEquals("FUTURE2", e2.getText(80)); + assertEquals(null, e3.getText(80)); + + assertEquals(before + 5000, e0.getTimeMillis()); + assertEquals(before + 100000, e1.getTimeMillis()); + assertEquals(before + 100001, e2.getTimeMillis()); + assertEquals(before + 100002, e3.getTimeMillis()); + + e0.close(); + e1.close(); + e2.close(); + e3.close(); + + // Write something to force a collapse + dropbox.addText("NotDropBoxTest", "FUTURE"); + e0 = dropbox.getNextEntry(null, before); + e1 = dropbox.getNextEntry(null, e0.getTimeMillis()); + e2 = dropbox.getNextEntry(null, e1.getTimeMillis()); + e3 = dropbox.getNextEntry(null, e2.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry("DropBoxTest", e3.getTimeMillis())); + + assertEquals("FUTURE0", e0.getText(80)); + assertEquals("FUTURE1", e1.getText(80)); + assertEquals("FUTURE2", e2.getText(80)); + assertEquals(null, e3.getText(80)); + + assertEquals(before + 5000, e0.getTimeMillis()); + assertEquals(before + 5001, e1.getTimeMillis()); + assertEquals(before + 5002, e2.getTimeMillis()); + assertEquals(before + 5003, e3.getTimeMillis()); + + e0.close(); + e1.close(); + e2.close(); + e3.close(); + service.stop(); + } + + public void testIsTagEnabled() throws Exception { + DropBoxManager dropbox = (DropBoxManager) getContext().getSystemService( + Context.DROPBOX_SERVICE); + long before = System.currentTimeMillis(); + dropbox.addText("DropBoxTest", "TEST-ENABLED"); + assertTrue(dropbox.isTagEnabled("DropBoxTest")); + + ContentResolver cr = getContext().getContentResolver(); + Settings.Secure.putString(cr, Settings.Secure.DROPBOX_TAG_PREFIX + "DropBoxTest", + "disabled"); + + dropbox.addText("DropBoxTest", "TEST-DISABLED"); + assertFalse(dropbox.isTagEnabled("DropBoxTest")); + + Settings.Secure.putString(cr, Settings.Secure.DROPBOX_TAG_PREFIX + "DropBoxTest", + ""); + + dropbox.addText("DropBoxTest", "TEST-ENABLED-AGAIN"); + assertTrue(dropbox.isTagEnabled("DropBoxTest")); + + DropBoxManager.Entry e0 = dropbox.getNextEntry("DropBoxTest", before); + DropBoxManager.Entry e1 = dropbox.getNextEntry("DropBoxTest", e0.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry("DropBoxTest", e1.getTimeMillis())); + + assertEquals("TEST-ENABLED", e0.getText(80)); + assertEquals("TEST-ENABLED-AGAIN", e1.getText(80)); + + e0.close(); + e1.close(); + } + + public void testGetNextEntry() throws Exception { + File dir = getEmptyDir("testGetNextEntry"); + DropBoxManagerService service = new DropBoxManagerService(getContext(), dir); + DropBoxManager dropbox = new DropBoxManager(service); + + long before = System.currentTimeMillis(); + dropbox.addText("DropBoxTest.A", "A0"); + dropbox.addText("DropBoxTest.B", "B0"); + dropbox.addText("DropBoxTest.A", "A1"); + + DropBoxManager.Entry a0 = dropbox.getNextEntry("DropBoxTest.A", before); + DropBoxManager.Entry a1 = dropbox.getNextEntry("DropBoxTest.A", a0.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry("DropBoxTest.A", a1.getTimeMillis())); + + DropBoxManager.Entry b0 = dropbox.getNextEntry("DropBoxTest.B", before); + assertTrue(null == dropbox.getNextEntry("DropBoxTest.B", b0.getTimeMillis())); + + DropBoxManager.Entry x0 = dropbox.getNextEntry(null, before); + DropBoxManager.Entry x1 = dropbox.getNextEntry(null, x0.getTimeMillis()); + DropBoxManager.Entry x2 = dropbox.getNextEntry(null, x1.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry(null, x2.getTimeMillis())); + + assertEquals("DropBoxTest.A", a0.getTag()); + assertEquals("DropBoxTest.A", a1.getTag()); + assertEquals("A0", a0.getText(80)); + assertEquals("A1", a1.getText(80)); + + assertEquals("DropBoxTest.B", b0.getTag()); + assertEquals("B0", b0.getText(80)); + + assertEquals("DropBoxTest.A", x0.getTag()); + assertEquals("DropBoxTest.B", x1.getTag()); + assertEquals("DropBoxTest.A", x2.getTag()); + assertEquals("A0", x0.getText(80)); + assertEquals("B0", x1.getText(80)); + assertEquals("A1", x2.getText(80)); + + a0.close(); + a1.close(); + b0.close(); + x0.close(); + x1.close(); + x2.close(); + service.stop(); + } + + public void testSizeLimits() throws Exception { + File dir = getEmptyDir("testSizeLimits"); + int blockSize = new StatFs(dir.getPath()).getBlockSize(); + + // Limit storage to 10 blocks + int kb = blockSize * 10 / 1024; + ContentResolver cr = getContext().getContentResolver(); + Settings.Secure.putString(cr, Settings.Secure.DROPBOX_QUOTA_KB, Integer.toString(kb)); + + // Three tags using a total of 12 blocks: + // DropBoxTest0 [ ][ ] + // DropBoxTest1 [x][ ][ ][ ][xxx(20 blocks)xxx] + // DropBoxTest2 [xxxxxxxxxx][ ][ ] + // + // The blocks marked "x" will be removed due to storage restrictions. + // Use random fill (so it doesn't compress), subtract a little for gzip overhead + + final int overhead = 64; + long before = System.currentTimeMillis(); + DropBoxManagerService service = new DropBoxManagerService(getContext(), dir); + DropBoxManager dropbox = new DropBoxManager(service); + + addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead); + addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead); + + addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead); + addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead); + addRandomEntry(dropbox, "DropBoxTest1", blockSize * 2 - overhead); + addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead); + addRandomEntry(dropbox, "DropBoxTest1", blockSize * 20 - overhead); + + addRandomEntry(dropbox, "DropBoxTest2", blockSize * 4 - overhead); + addRandomEntry(dropbox, "DropBoxTest2", blockSize - overhead); + addRandomEntry(dropbox, "DropBoxTest2", blockSize - overhead); + + DropBoxManager.Entry e0 = dropbox.getNextEntry(null, before); + DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis()); + DropBoxManager.Entry e2 = dropbox.getNextEntry(null, e1.getTimeMillis()); + DropBoxManager.Entry e3 = dropbox.getNextEntry(null, e2.getTimeMillis()); + DropBoxManager.Entry e4 = dropbox.getNextEntry(null, e3.getTimeMillis()); + DropBoxManager.Entry e5 = dropbox.getNextEntry(null, e4.getTimeMillis()); + DropBoxManager.Entry e6 = dropbox.getNextEntry(null, e5.getTimeMillis()); + DropBoxManager.Entry e7 = dropbox.getNextEntry(null, e6.getTimeMillis()); + DropBoxManager.Entry e8 = dropbox.getNextEntry(null, e7.getTimeMillis()); + DropBoxManager.Entry e9 = dropbox.getNextEntry(null, e8.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry(null, e9.getTimeMillis())); + + assertEquals("DropBoxTest0", e0.getTag()); + assertEquals("DropBoxTest0", e1.getTag()); + assertEquals(blockSize - overhead, getEntrySize(e0)); + assertEquals(blockSize - overhead, getEntrySize(e1)); + + assertEquals("DropBoxTest1", e2.getTag()); + assertEquals("DropBoxTest1", e3.getTag()); + assertEquals("DropBoxTest1", e4.getTag()); + assertEquals("DropBoxTest1", e5.getTag()); + assertEquals("DropBoxTest1", e6.getTag()); + assertEquals(-1, getEntrySize(e2)); // Tombstone + assertEquals(blockSize - overhead, getEntrySize(e3)); + assertEquals(blockSize * 2 - overhead, getEntrySize(e4)); + assertEquals(blockSize - overhead, getEntrySize(e5)); + assertEquals(-1, getEntrySize(e6)); + + assertEquals("DropBoxTest2", e7.getTag()); + assertEquals("DropBoxTest2", e8.getTag()); + assertEquals("DropBoxTest2", e9.getTag()); + assertEquals(-1, getEntrySize(e7)); // Tombstone + assertEquals(blockSize - overhead, getEntrySize(e8)); + assertEquals(blockSize - overhead, getEntrySize(e9)); + + e0.close(); + e1.close(); + e2.close(); + e3.close(); + e4.close(); + e5.close(); + e6.close(); + e7.close(); + e8.close(); + e9.close(); + + // Specifying a tag name skips tombstone records. + + DropBoxManager.Entry t0 = dropbox.getNextEntry("DropBoxTest1", before); + DropBoxManager.Entry t1 = dropbox.getNextEntry("DropBoxTest1", t0.getTimeMillis()); + DropBoxManager.Entry t2 = dropbox.getNextEntry("DropBoxTest1", t1.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry("DropBoxTest1", t2.getTimeMillis())); + + assertEquals("DropBoxTest1", t0.getTag()); + assertEquals("DropBoxTest1", t1.getTag()); + assertEquals("DropBoxTest1", t2.getTag()); + + assertEquals(blockSize - overhead, getEntrySize(t0)); + assertEquals(blockSize * 2 - overhead, getEntrySize(t1)); + assertEquals(blockSize - overhead, getEntrySize(t2)); + + t0.close(); + t1.close(); + t2.close(); + service.stop(); + } + + public void testAgeLimits() throws Exception { + File dir = getEmptyDir("testAgeLimits"); + int blockSize = new StatFs(dir.getPath()).getBlockSize(); + + // Limit storage to 10 blocks with an expiration of 1 second + int kb = blockSize * 10 / 1024; + ContentResolver cr = getContext().getContentResolver(); + Settings.Secure.putString(cr, Settings.Secure.DROPBOX_AGE_SECONDS, "1"); + Settings.Secure.putString(cr, Settings.Secure.DROPBOX_QUOTA_KB, Integer.toString(kb)); + + // Write one normal entry and another so big that it is instantly tombstoned + long before = System.currentTimeMillis(); + DropBoxManagerService service = new DropBoxManagerService(getContext(), dir); + DropBoxManager dropbox = new DropBoxManager(service); + + dropbox.addText("DropBoxTest", "TEST"); + addRandomEntry(dropbox, "DropBoxTest", blockSize * 20); + + // Verify that things are as expected + DropBoxManager.Entry e0 = dropbox.getNextEntry(null, before); + DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry(null, e1.getTimeMillis())); + + assertEquals("TEST", e0.getText(80)); + assertEquals(null, e1.getText(80)); + assertEquals(-1, getEntrySize(e1)); + + e0.close(); + e1.close(); + + // Wait a second and write another entry -- old ones should be expunged + Thread.sleep(2000); + dropbox.addText("DropBoxTest", "TEST1"); + + e0 = dropbox.getNextEntry(null, before); + assertTrue(null == dropbox.getNextEntry(null, e0.getTimeMillis())); + assertEquals("TEST1", e0.getText(80)); + e0.close(); + } + + public void testFileCountLimits() throws Exception { + File dir = getEmptyDir("testFileCountLimits"); + + DropBoxManagerService service = new DropBoxManagerService(getContext(), dir); + DropBoxManager dropbox = new DropBoxManager(service); + dropbox.addText("DropBoxTest", "TEST0"); + dropbox.addText("DropBoxTest", "TEST1"); + dropbox.addText("DropBoxTest", "TEST2"); + dropbox.addText("DropBoxTest", "TEST3"); + dropbox.addText("DropBoxTest", "TEST4"); + dropbox.addText("DropBoxTest", "TEST5"); + + // Verify 6 files added + DropBoxManager.Entry e0 = dropbox.getNextEntry(null, 0); + DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis()); + DropBoxManager.Entry e2 = dropbox.getNextEntry(null, e1.getTimeMillis()); + DropBoxManager.Entry e3 = dropbox.getNextEntry(null, e2.getTimeMillis()); + DropBoxManager.Entry e4 = dropbox.getNextEntry(null, e3.getTimeMillis()); + DropBoxManager.Entry e5 = dropbox.getNextEntry(null, e4.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry(null, e5.getTimeMillis())); + assertEquals("TEST0", e0.getText(80)); + assertEquals("TEST5", e5.getText(80)); + + e0.close(); + e1.close(); + e2.close(); + e3.close(); + e4.close(); + e5.close(); + + // Limit to 3 files and add one more entry + ContentResolver cr = getContext().getContentResolver(); + Settings.Secure.putString(cr, Settings.Secure.DROPBOX_MAX_FILES, "3"); + dropbox.addText("DropBoxTest", "TEST6"); + + // Verify only 3 files left + DropBoxManager.Entry f0 = dropbox.getNextEntry(null, 0); + DropBoxManager.Entry f1 = dropbox.getNextEntry(null, f0.getTimeMillis()); + DropBoxManager.Entry f2 = dropbox.getNextEntry(null, f1.getTimeMillis()); + assertTrue(null == dropbox.getNextEntry(null, f2.getTimeMillis())); + assertEquals("TEST4", f0.getText(80)); + assertEquals("TEST5", f1.getText(80)); + assertEquals("TEST6", f2.getText(80)); + + f0.close(); + f1.close(); + f2.close(); + } + + public void testCreateDropBoxManagerWithInvalidDirectory() throws Exception { + // If created with an invalid directory, the DropBoxManager should suffer quietly + // and fail all operations (this is how it survives a full disk). + // Once the directory becomes possible to create, it will start working. + + File dir = new File(getEmptyDir("testCreateDropBoxManagerWith"), "InvalidDirectory"); + new FileOutputStream(dir).close(); // Create an empty file + DropBoxManagerService service = new DropBoxManagerService(getContext(), dir); + DropBoxManager dropbox = new DropBoxManager(service); + + dropbox.addText("DropBoxTest", "should be ignored"); + dropbox.addData("DropBoxTest", "should be ignored".getBytes(), 0); + assertTrue(null == dropbox.getNextEntry("DropBoxTest", 0)); + + dir.delete(); // Remove the file so a directory can be created + dropbox.addText("DropBoxTest", "TEST"); + DropBoxManager.Entry e = dropbox.getNextEntry("DropBoxTest", 0); + assertTrue(null == dropbox.getNextEntry("DropBoxTest", e.getTimeMillis())); + assertEquals("DropBoxTest", e.getTag()); + assertEquals("TEST", e.getText(80)); + e.close(); + service.stop(); + } + + private void addRandomEntry(DropBoxManager dropbox, String tag, int size) throws Exception { + byte[] bytes = new byte[size]; + new Random(System.currentTimeMillis()).nextBytes(bytes); + + File f = new File(getEmptyDir("addRandomEntry"), "random.dat"); + FileOutputStream os = new FileOutputStream(f); + os.write(bytes); + os.close(); + + dropbox.addFile(tag, f, 0); + } + + private int getEntrySize(DropBoxManager.Entry e) throws Exception { + InputStream is = e.getInputStream(); + if (is == null) return -1; + int length = 0; + while (is.read() != -1) length++; + return length; + } + + private void recursiveDelete(File file) { + if (!file.delete() && file.isDirectory()) { + for (File f : file.listFiles()) recursiveDelete(f); + file.delete(); + } + } + + private File getEmptyDir(String name) { + File dir = getContext().getDir("DropBoxTest." + name, 0); + for (File f : dir.listFiles()) recursiveDelete(f); + assertTrue(dir.listFiles().length == 0); + return dir; + } +} diff --git a/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java b/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java new file mode 100644 index 0000000..636ba21 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.Context; +import android.os.FileUtils; +import android.test.AndroidTestCase; + +import java.io.File; + +/** + * Tests for {@link com.android.server.EntropyService} + */ +public class EntropyServiceTest extends AndroidTestCase { + + public void testInitialWrite() throws Exception { + File dir = getContext().getDir("testInitialWrite", Context.MODE_PRIVATE); + File file = File.createTempFile("testInitialWrite", "dat", dir); + file.deleteOnExit(); + assertEquals(0, FileUtils.readTextFile(file, 0, null).length()); + + // The constructor has the side effect of writing to file + new EntropyService("/dev/null", file.getCanonicalPath()); + + assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0); + } +} |