diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2009-07-21 11:16:54 -0700 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2009-07-21 11:16:54 -0700 |
commit | cf4550c3198d6b3d92cdc52707fe70d7cc0caa9f (patch) | |
tree | 6510f35ad004f1a4640b48264c290926e8596d7a /services | |
parent | 4cf03d381b2dff908857fceff0bec445f8d44f36 (diff) | |
download | frameworks_base-cf4550c3198d6b3d92cdc52707fe70d7cc0caa9f.zip frameworks_base-cf4550c3198d6b3d92cdc52707fe70d7cc0caa9f.tar.gz frameworks_base-cf4550c3198d6b3d92cdc52707fe70d7cc0caa9f.tar.bz2 |
donut snapshot
Diffstat (limited to 'services')
35 files changed, 5875 insertions, 1453 deletions
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java new file mode 100644 index 0000000..c205fc0 --- /dev/null +++ b/services/java/com/android/server/AccessibilityManagerService.java @@ -0,0 +1,668 @@ +/* + ** Copyright 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 static android.util.Config.LOGV; + +import com.android.internal.os.HandlerCaller; +import com.android.internal.os.HandlerCaller.SomeArgs; + +import android.accessibilityservice.AccessibilityService; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.IAccessibilityServiceConnection; +import android.accessibilityservice.IEventListener; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +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.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Binder; +import android.os.DeadObjectException; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.provider.Settings; +import android.text.TextUtils; +import android.text.TextUtils.SimpleStringSplitter; +import android.util.Log; +import android.util.SparseArray; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.IAccessibilityManager; +import android.view.accessibility.IAccessibilityManagerClient; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * This class is instantiated by the system as a system level service and can be + * accessed only by the system. The task of this service is to be a centralized + * event dispatch for {@link AccessibilityEvent}s generated across all processes + * on the device. Events are dispatched to {@link AccessibilityService}s. + * + * @hide + */ +public class AccessibilityManagerService extends IAccessibilityManager.Stub + implements HandlerCaller.Callback { + + private static final String LOG_TAG = "AccessibilityManagerService"; + + private static int sIdCounter = 0; + + private static final int OWN_PROCESS_ID = android.os.Process.myPid(); + + private static final int DO_SET_SERVICE_INFO = 10; + + final HandlerCaller mCaller; + + final Context mContext; + + final Object mLock = new Object(); + + final List<Service> mServices = new ArrayList<Service>(); + + final List<IAccessibilityManagerClient> mClients = + new ArrayList<IAccessibilityManagerClient>(); + + final Map<ComponentName, Service> mComponentNameToServiceMap = + new HashMap<ComponentName, Service>(); + + private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>(); + + private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>(); + + private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':'); + + private PackageManager mPackageManager; + + private int mHandledFeedbackTypes = 0; + + private boolean mIsEnabled; + + /** + * Handler for delayed event dispatch. + */ + private Handler mHandler = new Handler() { + + @Override + public void handleMessage(Message message) { + Service service = (Service) message.obj; + int eventType = message.arg1; + + synchronized (mLock) { + notifyEventListenerLocked(service, eventType); + AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType); + service.mPendingEvents.remove(eventType); + tryRecycleLocked(oldEvent); + } + } + }; + + /** + * Creates a new instance. + * + * @param context A {@link Context} instance. + */ + AccessibilityManagerService(Context context) { + mContext = context; + mPackageManager = mContext.getPackageManager(); + mCaller = new HandlerCaller(context, this); + + registerPackageChangeAndBootCompletedBroadcastReceiver(); + registerSettingsContentObservers(); + + synchronized (mLock) { + populateAccessibilityServiceListLocked(); + } + } + + /** + * Registers a {@link BroadcastReceiver} for the events of + * adding/changing/removing/restarting a package and boot completion. + */ + private void registerPackageChangeAndBootCompletedBroadcastReceiver() { + Context context = mContext; + + BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + populateAccessibilityServiceListLocked(); + manageServicesLocked(); + + if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { + mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; + updateClientsLocked(); + } + } + } + }; + + // 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); + + // boot completed + IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + mContext.registerReceiver(broadcastReceiver, bootFiler); + } + + /** + * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED} + * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings. + */ + private void registerSettingsContentObservers() { + ContentResolver contentResolver = mContext.getContentResolver(); + + Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED); + contentResolver.registerContentObserver(enabledUri, false, + new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + + mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; + + synchronized (mLock) { + if (mIsEnabled) { + manageServicesLocked(); + } else { + unbindAllServicesLocked(); + } + updateClientsLocked(); + } + } + }); + + Uri providersUri = + Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); + contentResolver.registerContentObserver(providersUri, false, + new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + + synchronized (mLock) { + manageServicesLocked(); + } + } + }); + } + + public void addClient(IAccessibilityManagerClient client) { + synchronized (mLock) { + try { + client.setEnabled(mIsEnabled); + mClients.add(client); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re); + } + } + } + + public boolean sendAccessibilityEvent(AccessibilityEvent event) { + synchronized (mLock) { + notifyAccessibilityServicesDelayedLocked(event, false); + notifyAccessibilityServicesDelayedLocked(event, true); + } + // event not scheduled for dispatch => recycle + if (mHandledFeedbackTypes == 0) { + event.recycle(); + } else { + mHandledFeedbackTypes = 0; + } + + return (OWN_PROCESS_ID != Binder.getCallingPid()); + } + + public List<ServiceInfo> getAccessibilityServiceList() { + synchronized (mLock) { + return mInstalledServices; + } + } + + public void interrupt() { + synchronized (mLock) { + for (int i = 0, count = mServices.size(); i < count; i++) { + Service service = mServices.get(i); + try { + service.mServiceInterface.onInterrupt(); + } catch (RemoteException re) { + if (re instanceof DeadObjectException) { + Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up."); + if (removeDeadServiceLocked(service)) { + count--; + i--; + } + } else { + Log.e(LOG_TAG, "Error during sending interrupt request to " + + service.mService, re); + } + } + } + } + } + + public void executeMessage(Message message) { + switch (message.what) { + case DO_SET_SERVICE_INFO: + SomeArgs arguments = ((SomeArgs) message.obj); + + AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1; + Service service = (Service) arguments.arg2; + + synchronized (mLock) { + service.mEventTypes = info.eventTypes; + service.mFeedbackType = info.feedbackType; + String[] packageNames = info.packageNames; + if (packageNames != null) { + service.mPackageNames.addAll(Arrays.asList(packageNames)); + } + service.mNotificationTimeout = info.notificationTimeout; + service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0; + } + return; + default: + Log.w(LOG_TAG, "Unknown message type: " + message.what); + } + } + + /** + * Populates the cached list of installed {@link AccessibilityService}s. + */ + private void populateAccessibilityServiceListLocked() { + mInstalledServices.clear(); + + List<ResolveInfo> installedServices = mPackageManager.queryIntentServices( + new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES); + + for (int i = 0, count = installedServices.size(); i < count; i++) { + mInstalledServices.add(installedServices.get(i).serviceInfo); + } + } + + /** + * Performs {@link AccessibilityService}s delayed notification. The delay is configurable + * and denotes the period after the last event before notifying the service. + * + * @param event The event. + * @param isDefault True to notify default listeners, not default services. + */ + private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, + boolean isDefault) { + for (int i = 0, count = mServices.size(); i < count; i++) { + Service service = mServices.get(i); + + if (service.mIsDefault == isDefault) { + if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) { + mHandledFeedbackTypes |= service.mFeedbackType; + notifyAccessibilityServiceDelayedLocked(service, event); + } + } + } + } + + /** + * Performs an {@link AccessibilityService} delayed notification. The delay is configurable + * and denotes the period after the last event before notifying the service. + * + * @param service The service. + * @param event The event. + */ + private void notifyAccessibilityServiceDelayedLocked(Service service, + AccessibilityEvent event) { + synchronized (mLock) { + int eventType = event.getEventType(); + AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType); + service.mPendingEvents.put(eventType, event); + + int what = eventType | (service.mId << 16); + if (oldEvent != null) { + mHandler.removeMessages(what); + tryRecycleLocked(oldEvent); + } + + Message message = mHandler.obtainMessage(what, service); + message.arg1 = event.getEventType(); + mHandler.sendMessageDelayed(message, service.mNotificationTimeout); + } + } + + /** + * Recycles an event if it can be safely recycled. The condition is that no + * not notified service is interested in the event. + * + * @param event The event. + */ + private void tryRecycleLocked(AccessibilityEvent event) { + int eventType = event.getEventType(); + List<Service> services = mServices; + + // linear in the number of service which is not large + for (int i = 0, count = services.size(); i < count; i++) { + Service service = services.get(i); + if (service.mPendingEvents.get(eventType) == event) { + return; + } + } + + event.recycle(); + } + + /** + * Notifies a service for a scheduled event given the event type. + * + * @param service The service. + * @param eventType The type of the event to dispatch. + */ + private void notifyEventListenerLocked(Service service, int eventType) { + IEventListener listener = service.mServiceInterface; + AccessibilityEvent event = service.mPendingEvents.get(eventType); + + try { + listener.onAccessibilityEvent(event); + if (LOGV) { + Log.i(LOG_TAG, "Event " + event + " sent to " + listener); + } + } catch (RemoteException re) { + if (re instanceof DeadObjectException) { + Log.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); + } + } + } + + /** + * Removes a dead service. + * + * @param service The service. + * @return True if the service was removed, false otherwise. + */ + private boolean removeDeadServiceLocked(Service service) { + mServices.remove(service); + mHandler.removeMessages(service.mId); + + if (LOGV) { + Log.i(LOG_TAG, "Dead service " + service.mService + " removed"); + } + + if (mServices.isEmpty()) { + mIsEnabled = false; + updateClientsLocked(); + } + + return true; + } + + /** + * Determines if given event can be dispatched to a service based on the package of the + * event source and already notified services for that event type. Specifically, a + * service is notified if it is interested in events from the package and no other service + * providing the same feedback type has been notified. Exception are services the + * provide generic feedback (feedback type left as a safety net for unforeseen feedback + * types) which are always notified. + * + * @param service The potential receiver. + * @param event The event. + * @param handledFeedbackTypes The feedback types for which services have been notified. + * @return True if the listener should be notified, false otherwise. + */ + private boolean canDispathEventLocked(Service service, AccessibilityEvent event, + int handledFeedbackTypes) { + + if (!service.isConfigured()) { + return false; + } + + if (!service.mService.isBinderAlive()) { + removeDeadServiceLocked(service); + return false; + } + + int eventType = event.getEventType(); + if ((service.mEventTypes & eventType) != eventType) { + return false; + } + + Set<String> packageNames = service.mPackageNames; + CharSequence packageName = event.getPackageName(); + + if (packageNames.isEmpty() || packageNames.contains(packageName)) { + int feedbackType = service.mFeedbackType; + if ((handledFeedbackTypes & feedbackType) != feedbackType + || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) { + return true; + } + } + + return false; + } + + /** + * Manages services by starting enabled ones and stopping disabled ones. + */ + private void manageServicesLocked() { + populateEnabledServicesLocked(mEnabledServices); + updateServicesStateLocked(mInstalledServices, mEnabledServices); + } + + /** + * Unbinds all bound services. + */ + private void unbindAllServicesLocked() { + List<Service> services = mServices; + + for (int i = 0, count = services.size(); i < count; i++) { + Service service = services.get(i); + + service.unbind(); + mComponentNameToServiceMap.remove(service.mComponentName); + } + services.clear(); + } + + /** + * Populates a list with the {@link ComponentName}s of all enabled + * {@link AccessibilityService}s. + * + * @param enabledServices The list. + */ + private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) { + enabledServices.clear(); + + String servicesValue = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); + + if (servicesValue != null) { + TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; + splitter.setString(servicesValue); + while (splitter.hasNext()) { + ComponentName enabledService = ComponentName.unflattenFromString(splitter.next()); + enabledServices.add(enabledService); + } + } + } + + /** + * Updates the state of each service by starting (or keeping running) enabled ones and + * stopping the rest. + * + * @param installedServices All installed {@link AccessibilityService}s. + * @param enabledServices The {@link ComponentName}s of the enabled services. + */ + private void updateServicesStateLocked(List<ServiceInfo> installedServices, + Set<ComponentName> enabledServices) { + + Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap; + List<Service> services = mServices; + + for (int i = 0, count = installedServices.size(); i < count; i++) { + ServiceInfo intalledService = installedServices.get(i); + ComponentName componentName = new ComponentName(intalledService.packageName, + intalledService.name); + Service service = componentNameToServiceMap.get(componentName); + + if (enabledServices.contains(componentName)) { + if (service == null) { + new Service(componentName).bind(); + } + } else { + if (service != null) { + service.unbind(); + componentNameToServiceMap.remove(componentName); + services.remove(service); + } + } + } + } + + /** + * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients. + */ + private void updateClientsLocked() { + for (int i = 0, count = mClients.size(); i < count; i++) { + try { + mClients.get(i).setEnabled(mIsEnabled); + } catch (RemoteException re) { + mClients.remove(i); + count--; + } + } + } + + /** + * This class represents an accessibility service. It stores all per service + * data required for the service management, provides API for starting/stopping the + * service and is responsible for adding/removing the service in the data structures + * for service management. The class also exposes configuration interface that is + * passed to the service it represents as soon it is bound. It also serves as the + * connection for the service. + */ + class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection { + int mId = 0; + + IBinder mService; + + IEventListener mServiceInterface; + + int mEventTypes; + + int mFeedbackType; + + Set<String> mPackageNames = new HashSet<String>(); + + boolean mIsDefault; + + long mNotificationTimeout; + + boolean mIsActive; + + ComponentName mComponentName; + + Intent mIntent; + + // the events pending events to be dispatched to this service + final SparseArray<AccessibilityEvent> mPendingEvents = + new SparseArray<AccessibilityEvent>(); + + Service(ComponentName componentName) { + mId = sIdCounter++; + mComponentName = componentName; + mIntent = new Intent().setComponent(mComponentName); + } + + /** + * Binds to the accessibility service. + */ + public void bind() { + if (mService == null) { + mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE); + } + } + + /** + * Unbinds form the accessibility service and removes it from the data + * structures for service management. + */ + public void unbind() { + if (mService != null) { + mContext.unbindService(this); + } + } + + /** + * Returns if the service is configured i.e. at least event types of interest + * and feedback type must be set. + * + * @return True if the service is configured, false otherwise. + */ + public boolean isConfigured() { + return (mEventTypes != 0 && mFeedbackType != 0); + } + + public void setServiceInfo(AccessibilityServiceInfo info) { + mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget(); + } + + public void onServiceConnected(ComponentName componentName, IBinder service) { + mService = service; + mServiceInterface = IEventListener.Stub.asInterface(service); + + try { + mServiceInterface.setConnection(this); + synchronized (mLock) { + if (!mServices.contains(this)) { + mServices.add(this); + mComponentNameToServiceMap.put(componentName, this); + } + } + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while setting Controller for service: " + service, re); + } + } + + public void onServiceDisconnected(ComponentName componentName) { + synchronized (mLock) { + Service service = mComponentNameToServiceMap.remove(componentName); + mServices.remove(service); + } + } + } +} diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index c50ae94..131e156 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -29,7 +29,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; -import android.content.pm.PackageItemInfo; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.net.Uri; @@ -40,6 +39,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; +import android.util.TypedValue; import android.util.Xml; import android.widget.RemoteViews; @@ -56,7 +56,6 @@ import java.util.HashSet; import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.appwidget.IAppWidgetHost; -import com.android.internal.util.XmlUtils; import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; @@ -79,7 +78,7 @@ class AppWidgetService extends IAppWidgetService.Stub static class Provider { int uid; AppWidgetProviderInfo info; - ArrayList<AppWidgetId> instances = new ArrayList(); + ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>(); PendingIntent broadcast; boolean zombie; // if we're in safe mode, don't prune this just because nobody references it @@ -90,7 +89,7 @@ class AppWidgetService extends IAppWidgetService.Stub int uid; int hostId; String packageName; - ArrayList<AppWidgetId> instances = new ArrayList(); + ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>(); IAppWidgetHost callbacks; boolean zombie; // if we're in safe mode, don't prune this just because nobody references it @@ -107,10 +106,10 @@ class AppWidgetService extends IAppWidgetService.Stub Context mContext; PackageManager mPackageManager; AlarmManager mAlarmManager; - ArrayList<Provider> mInstalledProviders = new ArrayList(); + ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>(); int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1; - ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList(); - ArrayList<Host> mHosts = new ArrayList(); + final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>(); + ArrayList<Host> mHosts = new ArrayList<Host>(); boolean mSafeMode; AppWidgetService(Context context) { @@ -174,7 +173,7 @@ class AppWidgetService extends IAppWidgetService.Stub for (int i=0; i<N; i++) { AppWidgetId id = mAppWidgetIds.get(i); pw.print(" ["); pw.print(i); pw.print("] id="); - pw.println(id.appWidgetId);; + pw.println(id.appWidgetId); pw.print(" hostId="); pw.print(id.host.hostId); pw.print(' '); pw.print(id.host.packageName); pw.print('/'); @@ -384,7 +383,7 @@ class AppWidgetService extends IAppWidgetService.Stub public List<AppWidgetProviderInfo> getInstalledProviders() { synchronized (mAppWidgetIds) { final int N = mInstalledProviders.size(); - ArrayList<AppWidgetProviderInfo> result = new ArrayList(N); + ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N); for (int i=0; i<N; i++) { Provider p = mInstalledProviders.get(i); if (!p.zombie) { @@ -619,7 +618,6 @@ class AppWidgetService extends IAppWidgetService.Stub // rely on the fact that we've already set it and that // PendingIntent.getBroadcast will update the extras. boolean alreadyRegistered = p.broadcast != null; - int instancesSize = p.instances.size(); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); intent.setComponent(p.info.provider); @@ -695,10 +693,16 @@ class AppWidgetService extends IAppWidgetService.Stub TypedArray sa = mContext.getResources().obtainAttributes(attrs, com.android.internal.R.styleable.AppWidgetProviderInfo); - info.minWidth = sa.getDimensionPixelSize( - com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth, 0); - info.minHeight = sa.getDimensionPixelSize( - com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight, 0); + + // These dimensions has to be resolved in the application's context. + // We simply send back the raw complex data, which will be + // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}. + TypedValue value = sa.peekValue( + com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth); + info.minWidth = value != null ? value.data : 0; + value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight); + info.minHeight = value != null ? value.data : 0; + info.updatePeriodMillis = sa.getInt( com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0); info.initialLayout = sa.getResourceId( @@ -773,10 +777,12 @@ class AppWidgetService extends IAppWidgetService.Stub if (real.exists()) { readStateFromFileLocked(real); if (temp.exists()) { + //noinspection ResultOfMethodCallIgnored temp.delete(); } } else if (temp.exists()) { readStateFromFileLocked(temp); + //noinspection ResultOfMethodCallIgnored temp.renameTo(real); } } @@ -792,18 +798,23 @@ class AppWidgetService extends IAppWidgetService.Stub // use the temporary one until it's fully written, create an empty file // for real, which will we'll shortly delete. try { + //noinspection ResultOfMethodCallIgnored real.createNewFile(); } catch (IOException e) { + // Ignore } } if (temp.exists()) { + //noinspection ResultOfMethodCallIgnored temp.delete(); } writeStateToFileLocked(temp); + //noinspection ResultOfMethodCallIgnored real.delete(); + //noinspection ResultOfMethodCallIgnored temp.renameTo(real); } @@ -866,8 +877,10 @@ class AppWidgetService extends IAppWidgetService.Stub stream.close(); } } catch (IOException ex) { + // Ignore } if (file.exists()) { + //noinspection ResultOfMethodCallIgnored file.delete(); } } @@ -885,7 +898,7 @@ class AppWidgetService extends IAppWidgetService.Stub int type; int providerIndex = 0; - HashMap<Integer,Provider> loadedProviders = new HashMap(); + HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>(); do { type = parser.next(); if (type == XmlPullParser.START_TAG) { @@ -986,6 +999,7 @@ class AppWidgetService extends IAppWidgetService.Stub stream.close(); } } catch (IOException e) { + // Ignore } if (success) { @@ -1081,7 +1095,7 @@ 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(); + HashSet<String> keep = new HashSet<String>(); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); @@ -1103,7 +1117,6 @@ class AppWidgetService extends IAppWidgetService.Stub if (parsed != null) { keep.add(ai.name); // Use the new AppWidgetProviderInfo. - AppWidgetProviderInfo oldInfo = p.info; p.info = parsed.info; // If it's enabled final int M = p.instances.size(); diff --git a/services/java/com/android/server/AttributeCache.java b/services/java/com/android/server/AttributeCache.java index 459ae52..81378dc 100644 --- a/services/java/com/android/server/AttributeCache.java +++ b/services/java/com/android/server/AttributeCache.java @@ -17,56 +17,36 @@ package com.android.server; -import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; -import android.content.SharedPreferences; -import android.content.Intent; -import android.content.BroadcastReceiver; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; -import android.provider.Settings; -import android.util.Config; -import android.util.Log; +import android.util.SparseArray; +import java.util.HashMap; import java.util.WeakHashMap; -public final class AttributeCache extends BroadcastReceiver { +/** + * TODO: This should be better integrated into the system so it doesn't need + * special calls from the activity manager to clear it. + */ +public final class AttributeCache { private static AttributeCache sInstance = null; private final Context mContext; - private final WeakHashMap<Key, Entry> mMap = - new WeakHashMap<Key, Entry>(); - private final WeakHashMap<String, Context> mContexts = - new WeakHashMap<String, Context>(); + private final WeakHashMap<String, Package> mPackages = + new WeakHashMap<String, Package>(); + private final Configuration mConfiguration = new Configuration(); - final static class Key { - public final String packageName; - public final int resId; - public final int[] styleable; - - public Key(String inPackageName, int inResId, int[] inStyleable) { - packageName = inPackageName; - resId = inResId; - styleable = inStyleable; - } + public final static class Package { + public final Context context; + private final SparseArray<HashMap<int[], Entry>> mMap + = new SparseArray<HashMap<int[], Entry>>(); - @Override public boolean equals(Object obj) { - try { - if (obj != null) { - Key other = (Key)obj; - return packageName.equals(other.packageName) - && resId == other.resId - && styleable == other.styleable; - } - } catch (ClassCastException e) { - } - return false; - } - - @Override public int hashCode() { - return packageName.hashCode() + resId; + public Package(Context c) { + context = c; } } @@ -94,36 +74,68 @@ public final class AttributeCache extends BroadcastReceiver { mContext = context; } - public Entry get(String packageName, int resId, int[] styleable) { + public void removePackage(String packageName) { synchronized (this) { - Key key = new Key(packageName, resId, styleable); - Entry ent = mMap.get(key); - if (ent != null) { - return ent; + mPackages.remove(packageName); + } + } + + public void updateConfiguration(Configuration config) { + synchronized (this) { + int changes = mConfiguration.updateFrom(config); + if ((changes & ~(ActivityInfo.CONFIG_FONT_SCALE | + ActivityInfo.CONFIG_KEYBOARD_HIDDEN | + ActivityInfo.CONFIG_ORIENTATION)) != 0) { + // The configurations being masked out are ones that commonly + // change so we don't want flushing the cache... all others + // will flush the cache. + mPackages.clear(); } - Context context = mContexts.get(packageName); - if (context == null) { + } + } + + public Entry get(String packageName, int resId, int[] styleable) { + synchronized (this) { + Package pkg = mPackages.get(packageName); + HashMap<int[], Entry> map = null; + Entry ent = null; + if (pkg != null) { + map = pkg.mMap.get(resId); + if (map != null) { + ent = map.get(styleable); + if (ent != null) { + return ent; + } + } + } else { + Context context; try { context = mContext.createPackageContext(packageName, 0); if (context == null) { return null; } - mContexts.put(packageName, context); } catch (PackageManager.NameNotFoundException e) { return null; } + pkg = new Package(context); + mPackages.put(packageName, pkg); } + + if (map == null) { + map = new HashMap<int[], Entry>(); + pkg.mMap.put(resId, map); + } + try { - ent = new Entry(context, - context.obtainStyledAttributes(resId, styleable)); - mMap.put(key, ent); + ent = new Entry(pkg.context, + pkg.context.obtainStyledAttributes(resId, styleable)); + map.put(styleable, ent); } catch (Resources.NotFoundException e) { return null; } + return ent; } } - @Override public void onReceive(Context context, Intent intent) { - } } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 983329b..3b82284 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -16,18 +16,26 @@ package com.android.server; -import android.backup.BackupService; -import android.backup.IBackupService; +import android.app.ActivityManagerNative; +import android.app.AlarmManager; +import android.app.IActivityManager; +import android.app.IApplicationThread; +import android.app.IBackupAgent; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageDataObserver; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; +import android.content.pm.Signature; import android.net.Uri; +import android.provider.Settings; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -35,71 +43,212 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.PowerManager; +import android.os.Process; import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; import android.backup.IBackupManager; +import android.backup.IRestoreObserver; +import android.backup.IRestoreSession; +import android.backup.RestoreSet; +import com.android.internal.backup.LocalTransport; +import com.android.internal.backup.IBackupTransport; + +import com.android.server.PackageManagerBackupAgent; +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.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; class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "BackupManagerService"; private static final boolean DEBUG = true; - - private static final long COLLECTION_INTERVAL = 1000; - //private static final long COLLECTION_INTERVAL = 3 * 60 * 1000; + // How often we perform a backup pass. Privileged external callers can + // trigger an immediate pass. + private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR; + + // The amount of time between the initial provisioning of the device and + // the first backup pass. + private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR; + + private static final String RUN_BACKUP_ACTION = "_backup_run_"; private static final 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; + + // Timeout interval for deciding that a bind or clear-data has taken too long + static final long TIMEOUT_INTERVAL = 10 * 1000; + private Context mContext; private PackageManager mPackageManager; + private IActivityManager mActivityManager; + private PowerManager mPowerManager; + private AlarmManager mAlarmManager; + + private boolean mEnabled; // access to this is synchronized on 'this' + private boolean mProvisioned; + private PowerManager.WakeLock mWakelock; private final BackupHandler mBackupHandler = new BackupHandler(); + private PendingIntent mRunBackupIntent; + private BroadcastReceiver mRunBackupReceiver; + private IntentFilter mRunBackupFilter; // map UIDs to the set of backup client services within that UID's app set - private SparseArray<HashSet<ServiceInfo>> mBackupParticipants - = new SparseArray<HashSet<ServiceInfo>>(); + private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants + = new SparseArray<HashSet<ApplicationInfo>>(); // set of backup services that have pending changes private class BackupRequest { - public ServiceInfo service; + public ApplicationInfo appInfo; public boolean fullBackup; - - BackupRequest(ServiceInfo svc, boolean isFull) { - service = svc; + + BackupRequest(ApplicationInfo app, boolean isFull) { + appInfo = app; fullBackup = isFull; } + + public String toString() { + return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}"; + } } // Backups that we haven't started yet. - private HashMap<ComponentName,BackupRequest> mPendingBackups = new HashMap(); - // Backups that we have started. These are separate to prevent starvation - // if an app keeps re-enqueuing itself. - private ArrayList<BackupRequest> mBackupQueue; + private HashMap<ApplicationInfo,BackupRequest> mPendingBackups + = new HashMap<ApplicationInfo,BackupRequest>(); + + // Pseudoname that we use for the Package Manager metadata "package" + private static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; + + // locking around the pending-backup management private final Object mQueueLock = new Object(); - private File mStateDir; + // The thread performing the sequence of queued backups binds to each app's agent + // in succession. Bind notifications are asynchronously delivered through the + // Activity Manager; use this lock object to signal when a requested binding has + // completed. + private final Object mAgentConnectLock = new Object(); + private IBackupAgent mConnectedAgent; + private volatile boolean mConnecting; + + // A similar synchronicity mechanism around clearing apps' data for restore + private final Object mClearDataLock = new Object(); + private volatile boolean mClearingData; + + // Transport bookkeeping + private final HashMap<String,IBackupTransport> mTransports + = new HashMap<String,IBackupTransport>(); + private String mCurrentTransport; + private IBackupTransport mLocalTransport, mGoogleTransport; + private RestoreSession mActiveRestoreSession; + + private class RestoreParams { + public IBackupTransport transport; + public IRestoreObserver observer; + public long token; + + RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) { + transport = _transport; + observer = _obs; + token = _token; + } + } + + private class ClearParams { + public IBackupTransport transport; + public PackageInfo packageInfo; + + ClearParams(IBackupTransport _transport, PackageInfo _info) { + transport = _transport; + packageInfo = _info; + } + } + + // Where we keep our journal files and other bookkeeping + private File mBaseStateDir; private File mDataDir; - + private File mJournalDir; + private File mJournal; + private RandomAccessFile mJournalStream; + public BackupManagerService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); + mActivityManager = ActivityManagerNative.getDefault(); + + mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); // Set up our bookkeeping - mStateDir = new File(Environment.getDataDirectory(), "backup"); - mStateDir.mkdirs(); + boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.BACKUP_ENABLED, 0) != 0; + // !!! TODO: mProvisioned needs to default to 0, not 1. + mProvisioned = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.BACKUP_PROVISIONED, 1) != 0; + mBaseStateDir = new File(Environment.getDataDirectory(), "backup"); mDataDir = Environment.getDownloadCacheDirectory(); - - // Build our mapping of uid to backup client services + + mRunBackupReceiver = new RunBackupReceiver(); + mRunBackupFilter = new IntentFilter(); + mRunBackupFilter.addAction(RUN_BACKUP_ACTION); + context.registerReceiver(mRunBackupReceiver, mRunBackupFilter); + + Intent backupIntent = new Intent(RUN_BACKUP_ACTION); + // !!! TODO: restrict delivery to our receiver; the naive setClass() doesn't seem to work + //backupIntent.setClass(context, mRunBackupReceiver.getClass()); + backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0); + + // Set up the backup-request journaling + mJournalDir = new File(mBaseStateDir, "pending"); + mJournalDir.mkdirs(); // creates mBaseStateDir along the way + makeJournalLocked(); // okay because no other threads are running yet + + // Build our mapping of uid to backup client services. This implicitly + // schedules a backup pass on the Package Manager metadata the first + // time anything needs to be backed up. synchronized (mBackupParticipants) { addPackageParticipantsLocked(null); } + // Set up our transport options and initialize the default transport + // TODO: Have transports register themselves somehow? + // TODO: Don't create transports that we don't need to? + mLocalTransport = new LocalTransport(context); // This is actually pretty cheap + ComponentName localName = new ComponentName(context, LocalTransport.class); + registerTransport(localName.flattenToShortString(), mLocalTransport); + + mGoogleTransport = null; + mCurrentTransport = Settings.Secure.getString(context.getContentResolver(), + Settings.Secure.BACKUP_TRANSPORT); + if ("".equals(mCurrentTransport)) { + mCurrentTransport = null; + } + if (DEBUG) Log.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); + + // Now that we know about valid backup participants, parse any + // leftover journal files into the pending backup set + parseLeftoverJournals(); + // Register for broadcasts about package install, etc., so we can // update the provider list. IntentFilter filter = new IntentFilter(); @@ -107,6 +256,77 @@ class BackupManagerService extends IBackupManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); mContext.registerReceiver(mBroadcastReceiver, filter); + + // Power management + mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup"); + + // Start the backup passes going + setBackupEnabled(areEnabled); + } + + private class RunBackupReceiver extends BroadcastReceiver { + public void onReceive(Context context, Intent intent) { + if (RUN_BACKUP_ACTION.equals(intent.getAction())) { + if (DEBUG) Log.v(TAG, "Running a backup pass"); + + synchronized (mQueueLock) { + // acquire a wakelock and pass it to the backup thread. it will + // be released once backup concludes. + mWakelock.acquire(); + + Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP); + mBackupHandler.sendMessage(msg); + } + } + } + } + + private void makeJournalLocked() { + try { + mJournal = File.createTempFile("journal", null, mJournalDir); + mJournalStream = new RandomAccessFile(mJournal, "rwd"); + } catch (IOException e) { + Log.e(TAG, "Unable to write backup journals"); + mJournal = null; + mJournalStream = null; + } + } + + private void parseLeftoverJournals() { + if (mJournal != null) { + File[] allJournals = mJournalDir.listFiles(); + for (File f : allJournals) { + if (f.compareTo(mJournal) != 0) { + // This isn't the current journal, so it must be a leftover. Read + // out the package names mentioned there and schedule them for + // backup. + try { + Log.i(TAG, "Found stale backup journal, scheduling:"); + RandomAccessFile in = new RandomAccessFile(f, "r"); + while (true) { + String packageName = in.readUTF(); + Log.i(TAG, " + " + packageName); + dataChanged(packageName); + } + } catch (EOFException e) { + // no more data; we're done + } catch (Exception e) { + // can't read it or other error; just skip it + } finally { + // close/delete the file + f.delete(); + } + } + } + } + } + + // Add a transport to our set of available backends + private void registerTransport(String name, IBackupTransport transport) { + synchronized (mTransports) { + if (DEBUG) Log.v(TAG, "Registering transport " + name + " = " + transport); + mTransports.put(name, transport); + } } // ----- Track installation/removal of packages ----- @@ -149,282 +369,1201 @@ 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"); + mGoogleTransport = IBackupTransport.Stub.asInterface(service); + registerTransport(name.flattenToShortString(), mGoogleTransport); + } + + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Log.v(TAG, "Disconnected from Google transport"); + mGoogleTransport = null; + registerTransport(name.flattenToShortString(), null); + } + }; + // ----- Run the actual backup process asynchronously ----- - private class BackupHandler extends Handler implements ServiceConnection { + private class BackupHandler extends Handler { public void handleMessage(Message msg) { switch (msg.what) { case MSG_RUN_BACKUP: + { + IBackupTransport transport = getTransport(mCurrentTransport); + if (transport == null) { + Log.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) { - mBackupQueue = new ArrayList(); - for (BackupRequest b: mPendingBackups.values()) { - mBackupQueue.add(b); + // 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 + if (mJournalStream != null) { + try { + mJournalStream.close(); + } catch (IOException e) { + // don't need to do anything + } + makeJournalLocked(); + } + + // 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"); + mWakelock.release(); } - mPendingBackups = new HashMap<ComponentName,BackupRequest>(); - // !!! TODO: start a new backup-queue journal file too - // WARNING: If we crash after this line, anything in mPendingBackups will - // be lost. FIX THIS. } - startOneService(); 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; + } + } } - - public void onServiceConnected(ComponentName name, IBinder service) { - Log.d(TAG, "onServiceConnected name=" + name + " service=" + service); - IBackupService bs = IBackupService.Stub.asInterface(service); - processOneBackup(name, bs); + } + + // 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); + List<PackageInfo> targetApps = allAgentPackages(); + addPackageParticipantsLockedInner(packageName, targetApps); + } + + private void addPackageParticipantsLockedInner(String packageName, + List<PackageInfo> targetPkgs) { + if (DEBUG) { + Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:"); + for (PackageInfo p : targetPkgs) { + Log.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName + + " uid=" + p.applicationInfo.uid); + } } - public void onServiceDisconnected(ComponentName name) { - // TODO: handle backup being interrupted + for (PackageInfo pkg : targetPkgs) { + if (packageName == null || pkg.packageName.equals(packageName)) { + int uid = pkg.applicationInfo.uid; + HashSet<ApplicationInfo> set = mBackupParticipants.get(uid); + if (set == null) { + set = new HashSet<ApplicationInfo>(); + mBackupParticipants.put(uid, set); + } + set.add(pkg.applicationInfo); + } } } - void startOneService() { - // Loop until we find someone to start or the queue empties out. - Intent intent = new Intent(BackupService.SERVICE_ACTION); - while (true) { - BackupRequest request; - synchronized (mQueueLock) { - int queueSize = mBackupQueue.size(); - if (queueSize == 0) { - mBackupQueue = null; - // TODO: Anything else to do here? - return; + // 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); + List<PackageInfo> allApps = null; + if (packageName != null) { + allApps = new ArrayList<PackageInfo>(); + try { + int flags = PackageManager.GET_SIGNATURES; + allApps.add(mPackageManager.getPackageInfo(packageName, flags)); + } catch (Exception e) { + // just skip it (???) + } + } else { + // all apps with agents + allApps = allAgentPackages(); + } + removePackageParticipantsLockedInner(packageName, allApps); + } + + private void removePackageParticipantsLockedInner(String packageName, + List<PackageInfo> agents) { + if (DEBUG) { + Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName + + ") removing " + agents.size() + " entries"); + for (PackageInfo p : agents) { + Log.v(TAG, " - " + p); + } + } + for (PackageInfo pkg : agents) { + if (packageName == null || pkg.packageName.equals(packageName)) { + int uid = pkg.applicationInfo.uid; + HashSet<ApplicationInfo> set = mBackupParticipants.get(uid); + if (set != null) { + // Find the existing entry with the same package name, and remove it. + // We can't just remove(app) because the instances are different. + for (ApplicationInfo entry: set) { + if (entry.packageName.equals(pkg.packageName)) { + set.remove(entry); + break; + } + } + if (set.size() == 0) { + mBackupParticipants.delete(uid); + } } - request = mBackupQueue.get(0); - // Take it off the queue when we're done. } - - intent.setClassName(request.service.packageName, request.service.name); - Log.d(TAG, "binding to " + intent); + } + } + + // Returns the set of all applications that define an android:backupAgent attribute + private List<PackageInfo> allAgentPackages() { + // !!! TODO: cache this and regenerate only when necessary + int flags = PackageManager.GET_SIGNATURES; + List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags); + int N = packages.size(); + for (int a = N-1; a >= 0; a--) { + ApplicationInfo app = packages.get(a).applicationInfo; + if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) + || app.backupAgentName == null) { + packages.remove(a); + } + } + return packages; + } + + // Reset the given package's known backup participants. Unlike add/remove, the update + // action cannot be passed a null package name. + void updatePackageParticipantsLocked(String packageName) { + if (packageName == null) { + Log.e(TAG, "updatePackageParticipants called with null package name"); + return; + } + if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName); + + // brute force but small code size + List<PackageInfo> allApps = allAgentPackages(); + removePackageParticipantsLockedInner(packageName, allApps); + addPackageParticipantsLockedInner(packageName, allApps); + } + + // 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); + } + return transport; + } + } + + // fire off a backup agent, blocking until it attaches or times out + IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) { + IBackupAgent agent = null; + synchronized(mAgentConnectLock) { + mConnecting = true; + mConnectedAgent = null; try { - if (mContext.bindService(intent, mBackupHandler, Context.BIND_AUTO_CREATE)) { - Log.d(TAG, "awaiting service object for " + intent); - // success - return; + if (mActivityManager.bindBackupAgent(app, mode)) { + Log.d(TAG, "awaiting agent for " + app); + + // success; wait for the agent to arrive + // only wait 10 seconds for the clear data to happen + long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; + while (mConnecting && mConnectedAgent == null + && (System.currentTimeMillis() < timeoutMark)) { + try { + mAgentConnectLock.wait(5000); + } catch (InterruptedException e) { + // just bail + return null; + } + } + + // if we timed out with no connect, abort and move on + if (mConnecting == true) { + Log.w(TAG, "Timeout waiting for agent " + app); + return null; + } + agent = mConnectedAgent; } - } catch (SecurityException ex) { - // Try for the next one. - Log.d(TAG, "error in bind", ex); + } catch (RemoteException e) { + // can't happen } } + return agent; } - void processOneBackup(ComponentName name, IBackupService bs) { + // clear an application's data, blocking until the operation completes or times out + void clearApplicationDataSynchronous(String packageName) { + // Don't wipe packages marked allowClearUserData=false try { - Log.d(TAG, "processOneBackup doBackup() on " + name); + 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 " + + packageName); + return; + } + } catch (NameNotFoundException e) { + Log.w(TAG, "Tried to clear data for " + packageName + " but not found"); + return; + } - BackupRequest request; - synchronized (mQueueLock) { - if (mBackupQueue == null) { - Log.d(TAG, "mBackupQueue is null. WHY?"); + ClearDataObserver observer = new ClearDataObserver(); + + synchronized(mClearDataLock) { + mClearingData = true; + mPackageManager.clearApplicationUserData(packageName, observer); + + // only wait 10 seconds for the clear data to happen + long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; + while (mClearingData && (System.currentTimeMillis() < timeoutMark)) { + try { + mClearDataLock.wait(5000); + } catch (InterruptedException e) { + // won't happen, but still. + mClearingData = false; } - request = mBackupQueue.get(0); } + } + } - // !!! TODO right now these naming schemes limit applications to - // one backup service per package - File savedStateName = new File(mStateDir, request.service.packageName); - File backupDataName = new File(mDataDir, request.service.packageName + ".data"); - File newStateName = new File(mStateDir, request.service.packageName + ".new"); - - // In a full backup, we pass a null ParcelFileDescriptor as - // the saved-state "file" - ParcelFileDescriptor savedState = (request.fullBackup) ? null - : ParcelFileDescriptor.open(savedStateName, - ParcelFileDescriptor.MODE_READ_ONLY | - ParcelFileDescriptor.MODE_CREATE); + class ClearDataObserver extends IPackageDataObserver.Stub { + public void onRemoveCompleted(String packageName, boolean succeeded) + throws android.os.RemoteException { + synchronized(mClearDataLock) { + mClearingData = false; + mClearDataLock.notifyAll(); + } + } + } - backupDataName.delete(); - ParcelFileDescriptor backupData = - ParcelFileDescriptor.open(backupDataName, - ParcelFileDescriptor.MODE_READ_WRITE | - ParcelFileDescriptor.MODE_CREATE); + // ----- Back up a set of applications via a worker thread ----- - newStateName.delete(); - ParcelFileDescriptor newState = - ParcelFileDescriptor.open(newStateName, - ParcelFileDescriptor.MODE_READ_WRITE | - ParcelFileDescriptor.MODE_CREATE); + class PerformBackupThread extends Thread { + private static final String TAG = "PerformBackupThread"; + IBackupTransport mTransport; + ArrayList<BackupRequest> mQueue; + File mStateDir; + File mJournal; + + public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue, + File journal) { + mTransport = transport; + mQueue = queue; + mJournal = journal; - // Run the target's backup pass try { - // TODO: Make this oneway - bs.doBackup(savedState, backupData, newState); - } finally { - if (savedState != null) { - savedState.close(); + mStateDir = new File(mBaseStateDir, transport.transportDirName()); + } catch (RemoteException e) { + // can't happen; the transport is local + } + mStateDir.mkdirs(); + } + + @Override + public void run() { + if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); + + // Backups run at background priority + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + + // The package manager doesn't have a proper <application> etc, but since + // it's running here in the system process we can just set up its agent + // directly and use a synthetic BackupRequest. We always run this pass + // because it's cheap and this way we guarantee that we don't get out of + // step even if we're selecting among various transports at run time. + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + mPackageManager, allAgentPackages()); + BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false); + pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL; + processOneBackup(pmRequest, + IBackupAgent.Stub.asInterface(pmAgent.onBind()), + mTransport); + + // Now run all the backups in our queue + doQueuedBackups(mTransport); + + // Finally, tear down the transport + try { + if (!mTransport.finishBackup()) { + // STOPSHIP TODO: handle errors + Log.e(TAG, "Backup failure in finishBackup()"); } - backupData.close(); - newState.close(); + } catch (RemoteException e) { + Log.e(TAG, "Error in finishBackup()", e); } - // !!! TODO: Now propagate the newly-backed-up data to the transport - - // !!! TODO: After successful transport, delete the now-stale data - // and juggle the files so that next time the new state is passed - backupDataName.delete(); - newStateName.renameTo(savedStateName); - - } catch (FileNotFoundException fnf) { - Log.d(TAG, "File not found on backup: "); - fnf.printStackTrace(); - } catch (RemoteException e) { - Log.d(TAG, "Remote target " + name + " threw during backup:"); - e.printStackTrace(); - } catch (Exception e) { - Log.w(TAG, "Final exception guard in backup: "); - e.printStackTrace(); + if (!mJournal.delete()) { + Log.e(TAG, "Unable to remove backup journal file " + mJournal.getAbsolutePath()); + } + + // Only once we're entirely finished do we release the wakelock + mWakelock.release(); } - synchronized (mQueueLock) { - mBackupQueue.remove(0); + + private void doQueuedBackups(IBackupTransport transport) { + for (BackupRequest request : mQueue) { + Log.d(TAG, "starting agent for backup of " + request); + + IBackupAgent agent = null; + int mode = (request.fullBackup) + ? IApplicationThread.BACKUP_MODE_FULL + : IApplicationThread.BACKUP_MODE_INCREMENTAL; + try { + agent = bindToAgentSynchronous(request.appInfo, mode); + if (agent != null) { + processOneBackup(request, agent, transport); + } + + // unbind even on timeout, just in case + mActivityManager.unbindBackupAgent(request.appInfo); + } catch (SecurityException ex) { + // Try for the next one. + Log.d(TAG, "error in bind/backup", ex); + } catch (RemoteException e) { + Log.v(TAG, "bind/backup threw"); + e.printStackTrace(); + } + + } } - mContext.unbindService(mBackupHandler); - } - // Add the backup services in the given package to our set of known backup participants. - // If 'packageName' is null, adds all backup services in the system. - void addPackageParticipantsLocked(String packageName) { - List<ResolveInfo> services = mPackageManager.queryIntentServices( - new Intent(BackupService.SERVICE_ACTION), 0); - addPackageParticipantsLockedInner(packageName, services); - } + void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) { + final String packageName = request.appInfo.packageName; + Log.d(TAG, "processOneBackup doBackup() on " + packageName); - private void addPackageParticipantsLockedInner(String packageName, List<ResolveInfo> services) { - for (ResolveInfo ri : services) { - if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) { - int uid = ri.serviceInfo.applicationInfo.uid; - HashSet<ServiceInfo> set = mBackupParticipants.get(uid); - if (set == null) { - set = new HashSet<ServiceInfo>(); - mBackupParticipants.put(uid, set); + try { + // Look up the package info & signatures. This is first so that if it + // throws an exception, there's no file setup yet that would need to + // be unraveled. + PackageInfo packInfo; + if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) { + // The metadata 'package' is synthetic + packInfo = new PackageInfo(); + packInfo.packageName = packageName; + } else { + packInfo = mPackageManager.getPackageInfo(packageName, + PackageManager.GET_SIGNATURES); } - if (DEBUG) { - Log.v(TAG, "Adding " + services.size() + " backup participants:"); - for (ResolveInfo svc : services) { - Log.v(TAG, " " + svc + " : " + svc.filter); + + // !!! TODO: get the state file dir from the transport + File savedStateName = new File(mStateDir, packageName); + File backupDataName = new File(mDataDir, packageName + ".data"); + File newStateName = new File(mStateDir, packageName + ".new"); + + // In a full backup, we pass a null ParcelFileDescriptor as + // the saved-state "file" + ParcelFileDescriptor savedState = (request.fullBackup) ? null + : ParcelFileDescriptor.open(savedStateName, + ParcelFileDescriptor.MODE_READ_ONLY | + ParcelFileDescriptor.MODE_CREATE); + + backupDataName.delete(); + ParcelFileDescriptor backupData = + ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + newStateName.delete(); + ParcelFileDescriptor newState = + ParcelFileDescriptor.open(newStateName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + // Run the target's backup pass + boolean success = false; + try { + agent.doBackup(savedState, backupData, newState); + success = true; + } finally { + if (savedState != null) { + savedState.close(); } + backupData.close(); + newState.close(); } - set.add(ri.serviceInfo); + // Now propagate the newly-backed-up data to the transport + if (success) { + if (DEBUG) Log.v(TAG, "doBackup() success; calling transport"); + backupData = + ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY); + if (!transport.performBackup(packInfo, backupData)) { + // STOPSHIP TODO: handle errors + Log.e(TAG, "Backup failure in performBackup()"); + } + + // !!! TODO: After successful transport, delete the now-stale data + // and juggle the files so that next time the new state is passed + //backupDataName.delete(); + newStateName.renameTo(savedStateName); + } + } catch (Exception e) { + Log.e(TAG, "Error backing up " + packageName, e); } } } - // Remove the given package's backup services from our known active set. If - // 'packageName' is null, *all* backup services will be removed. - void removePackageParticipantsLocked(String packageName) { - List<ResolveInfo> services = mPackageManager.queryIntentServices( - new Intent(BackupService.SERVICE_ACTION), 0); - removePackageParticipantsLockedInner(packageName, services); + + // ----- Restore handling ----- + + private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) { + // Allow unsigned apps, but not signed on one device and unsigned on the other + // !!! TODO: is this the right policy? + if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs + + " device=" + deviceSigs); + if ((storedSigs == null || storedSigs.length == 0) + && (deviceSigs == null || deviceSigs.length == 0)) { + return true; + } + if (storedSigs == null || deviceSigs == null) { + return false; + } + + // !!! TODO: this demands that every stored signature match one + // that is present on device, and does not demand the converse. + // Is this this right policy? + int nStored = storedSigs.length; + int nDevice = deviceSigs.length; + + for (int i=0; i < nStored; i++) { + boolean match = false; + for (int j=0; j < nDevice; j++) { + if (storedSigs[i].equals(deviceSigs[j])) { + match = true; + break; + } + } + if (!match) { + return false; + } + } + return true; } - private void removePackageParticipantsLockedInner(String packageName, - List<ResolveInfo> services) { - for (ResolveInfo ri : services) { - if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) { - int uid = ri.serviceInfo.applicationInfo.uid; - HashSet<ServiceInfo> set = mBackupParticipants.get(uid); - if (set != null) { - set.remove(ri.serviceInfo); - if (set.size() == 0) { - mBackupParticipants.put(uid, null); + class PerformRestoreThread extends Thread { + private IBackupTransport mTransport; + private IRestoreObserver mObserver; + private long mToken; + private RestoreSet mImage; + private File mStateDir; + + class RestoreRequest { + public PackageInfo app; + public int storedAppVersion; + + RestoreRequest(PackageInfo _app, int _version) { + app = _app; + storedAppVersion = _version; + } + } + + PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer, + long restoreSetToken) { + mTransport = transport; + Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver); + mObserver = observer; + mToken = restoreSetToken; + + try { + mStateDir = new File(mBaseStateDir, transport.transportDirName()); + } catch (RemoteException e) { + // can't happen; the transport is local + } + mStateDir.mkdirs(); + } + + @Override + public void run() { + if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport + + " mObserver=" + mObserver + " mToken=" + mToken); + /** + * Restore sequence: + * + * 1. get the restore set description for our identity + * 2. for each app in the restore set: + * 3.a. if it's restorable on this device, add it to the restore queue + * 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 + */ + + int error = -1; // assume error + + // build the set of apps to restore + try { + RestoreSet[] images = mTransport.getAvailableRestoreSets(); + if (images == null) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error getting restore sets"); + return; + } + + if (images.length == 0) { + Log.i(TAG, "No restore sets available"); + return; + } + + mImage = images[0]; + + // Get the list of all packages which have backup enabled. + // (Include the Package Manager metadata pseudo-package first.) + ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>(); + PackageInfo omPackage = new PackageInfo(); + omPackage.packageName = PACKAGE_MANAGER_SENTINEL; + restorePackages.add(omPackage); + + List<PackageInfo> agentPackages = allAgentPackages(); + restorePackages.addAll(agentPackages); + + // let the observer know that we're running + if (mObserver != null) { + try { + // !!! TODO: get an actual count from the transport after + // its startRestore() runs? + mObserver.restoreStarting(restorePackages.size()); + } catch (RemoteException e) { + Log.d(TAG, "Restore observer died at restoreStarting"); + mObserver = null; + } + } + + if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error starting restore operation"); + return; + } + + String packageName = mTransport.nextRestorePackage(); + if (packageName == null) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error getting first restore package"); + return; + } else if (packageName.equals("")) { + Log.i(TAG, "No restore data available"); + return; + } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) { + Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL + + "\", found only \"" + packageName + "\""); + return; + } + + // Pull the Package Manager metadata from the restore set first + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + mPackageManager, agentPackages); + processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind())); + + // Verify that the backup set includes metadata. If not, we can't do + // signature/version verification etc, so we simply do not proceed with + // the restore operation. + if (!pmAgent.hasMetadata()) { + Log.i(TAG, "No restore metadata available, so not restoring settings"); + return; + } + + int count = 0; + for (;;) { + packageName = mTransport.nextRestorePackage(); + if (packageName == null) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error getting next restore package"); + return; + } else if (packageName.equals("")) { + break; + } + + if (mObserver != null) { + ++count; + try { + mObserver.onUpdate(count); + } catch (RemoteException e) { + Log.d(TAG, "Restore observer died in onUpdate"); + mObserver = null; + } + } + + Metadata metaInfo = pmAgent.getRestoredMetadata(packageName); + if (metaInfo == null) { + Log.e(TAG, "Missing metadata for " + packageName); + continue; + } + + int flags = PackageManager.GET_SIGNATURES; + PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags); + if (metaInfo.versionCode > packageInfo.versionCode) { + Log.w(TAG, "Package " + packageName + + " restore version [" + metaInfo.versionCode + + "] is too new for installed version [" + + packageInfo.versionCode + "]"); + continue; + } + + if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) { + Log.w(TAG, "Signature mismatch restoring " + packageName); + continue; + } + + if (DEBUG) Log.v(TAG, "Package " + packageName + + " restore version [" + metaInfo.versionCode + + "] is compatible with installed version [" + + packageInfo.versionCode + "]"); + + // Now perform the actual restore + clearApplicationDataSynchronous(packageName); + IBackupAgent agent = bindToAgentSynchronous( + packageInfo.applicationInfo, + IApplicationThread.BACKUP_MODE_RESTORE); + if (agent == null) { + Log.w(TAG, "Can't find backup agent for " + packageName); + continue; + } + + try { + processOneRestore(packageInfo, metaInfo.versionCode, agent); + } finally { + // unbind even on timeout or failure, just in case + mActivityManager.unbindBackupAgent(packageInfo.applicationInfo); } } + + // if we get this far, report success to the observer + error = 0; + } catch (NameNotFoundException e) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Invalid paackage restoring data", e); + } catch (RemoteException e) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error restoring data", e); + } finally { + try { + mTransport.finishRestore(); + } catch (RemoteException e) { + Log.e(TAG, "Error finishing restore", e); + } + + Log.d(TAG, "finishing restore mObserver=" + mObserver); + + if (mObserver != null) { + try { + mObserver.restoreFinished(error); + } catch (RemoteException e) { + Log.d(TAG, "Restore observer died at restoreFinished"); + } + } + + // done; we can finally release the wakelock + mWakelock.release(); + } + } + + // Do the guts of a restore of one application, using mTransport.getRestoreData(). + void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) { + // !!! TODO: actually run the restore through mTransport + final String packageName = app.packageName; + + Log.d(TAG, "processOneRestore packageName=" + packageName); + + // !!! TODO: get the dirs from the transport + File backupDataName = new File(mDataDir, packageName + ".restore"); + backupDataName.delete(); + try { + ParcelFileDescriptor backupData = + ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + // Run the transport's restore pass + // Run the target's backup pass + try { + if (!mTransport.getRestoreData(backupData)) { + // STOPSHIP TODO: Handle this error somehow? + Log.e(TAG, "Error getting restore data for " + packageName); + return; + } + } finally { + backupData.close(); + } + + // Okay, we have the data. Now have the agent do the restore. + File newStateName = new File(mStateDir, packageName + ".new"); + ParcelFileDescriptor newState = + ParcelFileDescriptor.open(newStateName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + backupData = ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_ONLY); + + try { + agent.doRestore(backupData, appVersionCode, newState); + } finally { + newState.close(); + backupData.close(); + } + + // if everything went okay, remember the recorded state now + File savedStateName = new File(mStateDir, packageName); + newStateName.renameTo(savedStateName); + } catch (Exception e) { + Log.e(TAG, "Error restoring data for " + packageName, e); } } } - // Reset the given package's known backup participants. Unlike add/remove, the update - // action cannot be passed a null package name. - void updatePackageParticipantsLocked(String packageName) { - if (packageName == null) { - Log.e(TAG, "updatePackageParticipants called with null package name"); - return; + class PerformClearThread extends Thread { + IBackupTransport mTransport; + PackageInfo mPackage; + + PerformClearThread(IBackupTransport transport, PackageInfo packageInfo) { + mTransport = transport; + mPackage = packageInfo; } - // brute force but small code size - List<ResolveInfo> services = mPackageManager.queryIntentServices( - new Intent(BackupService.SERVICE_ACTION), 0); - removePackageParticipantsLockedInner(packageName, services); - addPackageParticipantsLockedInner(packageName, services); + @Override + public void run() { + try { + // Clear the on-device backup state to ensure a full backup next time + File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); + File stateFile = new File(stateDir, mPackage.packageName); + stateFile.delete(); + + // Tell the transport to remove all the persistent storage for the app + mTransport.clearBackupData(mPackage); + } catch (RemoteException e) { + // can't happen; the transport is local + } finally { + try { + mTransport.finishBackup(); + } catch (RemoteException e) { + // can't happen; the transport is local + } + + // Last but not least, release the cpu + mWakelock.release(); + } + } } + // ----- IBackupManager binder interface ----- - + public void dataChanged(String packageName) throws RemoteException { // 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. - Log.d(TAG, "dataChanged packageName=" + packageName); - - HashSet<ServiceInfo> targets = mBackupParticipants.get(Binder.getCallingUid()); - Log.d(TAG, "targets=" + targets); + // If the caller does not hold the BACKUP permission, it can only request a + // backup of its own data. + HashSet<ApplicationInfo> targets; + if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), + Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { + targets = mBackupParticipants.get(Binder.getCallingUid()); + } else { + // a caller with full permission can ask to back up any participating app + // !!! TODO: allow backup of ANY app? + targets = new HashSet<ApplicationInfo>(); + int N = mBackupParticipants.size(); + for (int i = 0; i < N; i++) { + HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i); + if (s != null) { + targets.addAll(s); + } + } + } if (targets != null) { synchronized (mQueueLock) { // Note that this client has made data changes that need to be backed up - for (ServiceInfo service : targets) { + for (ApplicationInfo app : targets) { // validate the caller-supplied package name against the known set of // packages associated with this uid - if (service.packageName.equals(packageName)) { + if (app.packageName.equals(packageName)) { // Add the caller to the set of pending backups. If there is // one already there, then overwrite it, but no harm done. - mPendingBackups.put(new ComponentName(service.packageName, service.name), - new BackupRequest(service, true)); - // !!! TODO: write to the pending-backup journal file in case of crash + BackupRequest req = new BackupRequest(app, false); + if (mPendingBackups.put(app, req) == null) { + // Journal this request in case of crash. The put() + // operation returned null when this package was not already + // in the set; we want to avoid touching the disk redundantly. + writeToJournalLocked(packageName); + + if (DEBUG) { + int numKeys = mPendingBackups.size(); + Log.d(TAG, "Now awaiting backup for " + numKeys + " participants:"); + for (BackupRequest b : mPendingBackups.values()) { + Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName); + } + } + } } } + } + } else { + Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"); + } + } + + private void writeToJournalLocked(String str) { + if (mJournalStream != null) { + try { + mJournalStream.writeUTF(str); + } catch (IOException e) { + Log.e(TAG, "Error writing to backup journal"); + mJournalStream = null; + mJournal = null; + } + } + } + + // Clear the given package's backup data from the current transport + public void clearBackupData(String packageName) { + if (DEBUG) Log.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"); + return; + } + + // If the caller does not hold the BACKUP permission, it can only request a + // wipe of its own backed-up data. + HashSet<ApplicationInfo> apps; + if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), + Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { + apps = mBackupParticipants.get(Binder.getCallingUid()); + } 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"); + apps = new HashSet<ApplicationInfo>(); + int N = mBackupParticipants.size(); + for (int i = 0; i < N; i++) { + HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i); + if (s != null) { + apps.addAll(s); + } + } + } + + // 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"); + // found it; fire off the clear request + synchronized (mQueueLock) { + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, + new ClearParams(getTransport(mCurrentTransport), info)); + mBackupHandler.sendMessage(msg); + } + break; + } + } + } - Log.d(TAG, "Scheduling backup for " + mPendingBackups.size() + " participants"); - // Schedule a backup pass in a few minutes. As backup-eligible data - // keeps changing, continue to defer the backup pass until things - // settle down, to avoid extra overhead. - mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL); + // Run a backup pass immediately for any applications that have declared + // that they have pending updates. + public void backupNow() throws RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "backupNow"); + + if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass"); + synchronized (mQueueLock) { + try { + if (DEBUG) Log.v(TAG, "sending immediate backup broadcast"); + mRunBackupIntent.send(); + } catch (PendingIntent.CanceledException e) { + // should never happen + Log.e(TAG, "run-backup intent cancelled!"); } } } - // Schedule a backup pass for a given package, even if the caller is not part of - // that uid or package itself. - public void scheduleFullBackup(String packageName) throws RemoteException { - // !!! TODO: protect with a signature-or-system permission? - HashSet<ServiceInfo> targets = new HashSet<ServiceInfo>(); + // Enable/disable the backup service + public void setBackupEnabled(boolean enable) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, + "setBackupEnabled"); + + boolean wasEnabled = mEnabled; + synchronized (this) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0); + mEnabled = enable; + } + synchronized (mQueueLock) { - int numKeys = mBackupParticipants.size(); - for (int index = 0; index < numKeys; index++) { - int uid = mBackupParticipants.keyAt(index); - HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid); - for (ServiceInfo service: servicesAtUid) { - if (service.packageName.equals(packageName)) { - mPendingBackups.put(new ComponentName(service.packageName, service.name), - new BackupRequest(service, true)); + if (enable && !wasEnabled && mProvisioned) { + // if we've just been enabled, start scheduling backup passes + startBackupAlarmsLocked(BACKUP_INTERVAL); + } else if (!enable) { + // No longer enabled, so stop running backups + mAlarmManager.cancel(mRunBackupIntent); + } + } + } + + // Mark the backup service as having been provisioned + public void setBackupProvisioned(boolean available) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, + "setBackupProvisioned"); + + boolean wasProvisioned = mProvisioned; + synchronized (this) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0); + mProvisioned = available; + } + + synchronized (mQueueLock) { + if (available && !wasProvisioned && mEnabled) { + // we're now good to go, so start the backup alarms + startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL); + } else if (!available) { + // No longer enabled, so stop running backups + Log.w(TAG, "Backup service no longer provisioned"); + mAlarmManager.cancel(mRunBackupIntent); + } + } + } + + private void startBackupAlarmsLocked(long delayBeforeFirstBackup) { + long when = System.currentTimeMillis() + delayBeforeFirstBackup; + mAlarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, when, + BACKUP_INTERVAL, mRunBackupIntent); + } + + // Report whether the backup mechanism is currently enabled + public boolean isBackupEnabled() { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); + return mEnabled; // no need to synchronize just to read it + } + + // Report the name of the currently active transport + public String getCurrentTransport() { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, + "getCurrentTransport"); + Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); + return mCurrentTransport; + } + + // Report all known, available backup transports + public String[] listAllTransports() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); + + String[] list = null; + ArrayList<String> known = new ArrayList<String>(); + for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { + if (entry.getValue() != null) { + known.add(entry.getKey()); + } + } + + if (known.size() > 0) { + list = new String[known.size()]; + known.toArray(list); + } + return list; + } + + // Select which transport to use for the next backup operation. If the given + // name is not one of the available transports, no action is taken and the method + // returns null. + public String selectBackupTransport(String transport) { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "selectBackupTransport"); + + synchronized (mTransports) { + String prevTransport = null; + if (mTransports.get(transport) != null) { + prevTransport = mCurrentTransport; + mCurrentTransport = transport; + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.BACKUP_TRANSPORT, transport); + Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport + + " returning " + prevTransport); + } else { + Log.w(TAG, "Attempt to select unavailable transport " + transport); + } + return prevTransport; + } + } + + // Callback: a requested backup agent has been instantiated. This should only + // be called from the Activity Manager. + public void agentConnected(String packageName, IBinder agentBinder) { + synchronized(mAgentConnectLock) { + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + Log.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() + + " claiming agent connected"); + } + mAgentConnectLock.notifyAll(); + } + } + + // Callback: a backup agent has failed to come up, or has unexpectedly quit. + // If the agent failed to come up in the first place, the agentBinder argument + // will be null. This should only be called from the Activity Manager. + public void agentDisconnected(String packageName) { + // TODO: handle backup being interrupted + synchronized(mAgentConnectLock) { + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + mConnectedAgent = null; + mConnecting = false; + } else { + Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid() + + " claiming agent disconnected"); + } + mAgentConnectLock.notifyAll(); + } + } + + // Hand off a restore session + public IRestoreSession beginRestoreSession(String transport) { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "beginRestoreSession"); + + synchronized(this) { + if (mActiveRestoreSession != null) { + Log.d(TAG, "Restore session requested but one already active"); + return null; + } + mActiveRestoreSession = new RestoreSession(transport); + } + return mActiveRestoreSession; + } + + // ----- Restore session ----- + + class RestoreSession extends IRestoreSession.Stub { + private static final String TAG = "RestoreSession"; + + private IBackupTransport mRestoreTransport = null; + RestoreSet[] mRestoreSets = null; + + RestoreSession(String transport) { + mRestoreTransport = getTransport(transport); + } + + // --- Binder interface --- + public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, + "getAvailableRestoreSets"); + + try { + synchronized(this) { + if (mRestoreSets == null) { + mRestoreSets = mRestoreTransport.getAvailableRestoreSets(); + } + return mRestoreSets; + } + } catch (RuntimeException e) { + Log.d(TAG, "getAvailableRestoreSets exception"); + e.printStackTrace(); + throw e; + } + } + + public int performRestore(long token, IRestoreObserver observer) + throws android.os.RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "performRestore"); + + Log.d(TAG, "performRestore token=" + token + " observer=" + observer); + + if (mRestoreSets != null) { + for (int i = 0; i < mRestoreSets.length; i++) { + if (token == mRestoreSets[i].token) { + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); + msg.obj = new RestoreParams(mRestoreTransport, observer, token); + mBackupHandler.sendMessage(msg); + return 0; } } + } else { + if (DEBUG) Log.v(TAG, "No current restore set, not doing restore"); + } + return -1; + } + + public void endRestoreSession() throws android.os.RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, + "endRestoreSession"); + + Log.d(TAG, "endRestoreSession"); + + mRestoreTransport.finishRestore(); + mRestoreTransport = null; + synchronized(BackupManagerService.this) { + if (BackupManagerService.this.mActiveRestoreSession == this) { + BackupManagerService.this.mActiveRestoreSession = null; + } else { + Log.e(TAG, "ending non-current restore session"); + } } } } - + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mQueueLock) { + long oldId = Binder.clearCallingIdentity(); + + pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") + + " / " + (!mProvisioned ? "not " : "") + "provisioned"); + pw.println("Available transports:"); + for (String t : listAllTransports()) { + String pad = (t.equals(mCurrentTransport)) ? " * " : " "; + pw.println(pad + t); + } int N = mBackupParticipants.size(); - pw.println("Participants:"); + pw.println("Participants: " + N); for (int i=0; i<N; i++) { int uid = mBackupParticipants.keyAt(i); pw.print(" uid: "); pw.println(uid); - HashSet<ServiceInfo> services = mBackupParticipants.valueAt(i); - for (ServiceInfo s: services) { - pw.print(" "); - pw.println(s.toString()); + HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i); + for (ApplicationInfo app: participants) { + pw.println(" " + app.toString()); } } + pw.println("Pending: " + mPendingBackups.size()); + for (BackupRequest req : mPendingBackups.values()) { + pw.println(" " + req); + } + + Binder.restoreCallingIdentity(oldId); } } } diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java new file mode 100644 index 0000000..28f09f5 --- /dev/null +++ b/services/java/com/android/server/EntropyService.java @@ -0,0 +1,148 @@ +/* + * 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.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; + +import android.os.Binder; +import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.os.SystemProperties; +import android.util.Log; + +/** + * A service designed to load and periodically save "randomness" + * for the Linux kernel. + * + * <p>When a Linux system starts up, the entropy pool associated with + * {@code /dev/random} may be in a fairly predictable state. Applications which + * depend strongly on randomness may find {@code /dev/random} or + * {@code /dev/urandom} returning predictable data. In order to counteract + * this effect, it's helpful to carry the entropy pool information across + * shutdowns and startups. + * + * <p>This class was modeled after the script in + * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man + * 4 random</a>. + * + * <p>TODO: Investigate attempting to write entropy data at shutdown time + * 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(); + + /** + * Handler that periodically updates the entropy on disk. + */ + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (msg.what != ENTROPY_WHAT) { + Log.e(TAG, "Will not process invalid message"); + return; + } + writeEntropy(); + scheduleEntropyWriter(); + } + }; + + public EntropyService() { + loadInitialEntropy(); + addDeviceSpecificEntropy(); + writeEntropy(); + scheduleEntropyWriter(); + } + + private void scheduleEntropyWriter() { + mHandler.removeMessages(ENTROPY_WHAT); + mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD); + } + + private void loadInitialEntropy() { + try { + RandomBlock.fromFile(ENTROPY_FILENAME).toFile(RANDOM_DEV); + } catch (IOException e) { + Log.w(TAG, "unable to load initial entropy (first boot?)", e); + } + } + + private void writeEntropy() { + try { + RandomBlock.fromFile(RANDOM_DEV).toFile(ENTROPY_FILENAME); + } catch (IOException e) { + Log.w(TAG, "unable to write entropy", e); + } + } + + /** + * Add additional information to the kernel entropy pool. The + * information isn't necessarily "random", but that's ok. Even + * sending non-random information to {@code /dev/urandom} is useful + * because, while it doesn't increase the "quality" of the entropy pool, + * it mixes more bits into the pool, which gives us a higher degree + * of uncertainty in the generated randomness. Like nature, writes to + * the random device can only cause the quality of the entropy in the + * kernel to stay the same or increase. + * + * <p>For maximum effect, we try to target information which varies + * on a per-device basis, and is not easily observable to an + * attacker. + */ + private void addDeviceSpecificEntropy() { + PrintWriter out = null; + try { + out = new PrintWriter(new FileOutputStream(RANDOM_DEV)); + out.println("Copyright (C) 2009 The Android Open Source Project"); + out.println("All Your Randomness Are Belong To Us"); + out.println(START_TIME); + out.println(START_NANOTIME); + out.println(SystemProperties.get("ro.serialno")); + out.println(SystemProperties.get("ro.bootmode")); + out.println(SystemProperties.get("ro.baseband")); + out.println(SystemProperties.get("ro.carrier")); + out.println(SystemProperties.get("ro.bootloader")); + out.println(SystemProperties.get("ro.hardware")); + out.println(SystemProperties.get("ro.revision")); + 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); + } finally { + if (out != null) { + out.close(); + } + } + } + + private static String getSystemDir() { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + systemDir.mkdirs(); + return systemDir.toString(); + } +} diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java index 3fc1e0e..9b0a2d4 100644 --- a/services/java/com/android/server/HeadsetObserver.java +++ b/services/java/com/android/server/HeadsetObserver.java @@ -35,6 +35,7 @@ import java.io.FileNotFoundException; */ class HeadsetObserver extends UEventObserver { private static final String TAG = HeadsetObserver.class.getSimpleName(); + private static final boolean LOG = false; private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w"; private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state"; @@ -61,7 +62,7 @@ class HeadsetObserver extends UEventObserver { @Override public void onUEvent(UEventObserver.UEvent event) { - Log.v(TAG, "Headset UEVENT: " + event.toString()); + if (LOG) Log.v(TAG, "Headset UEVENT: " + event.toString()); try { update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE"))); diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index 72efca5..d8c8c90 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -163,8 +163,24 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { return Collections.unmodifiableSet(mFilters); } - public List<R> queryIntent(ContentResolver resolver, Intent intent, - String resolvedType, boolean defaultOnly) { + public List<R> queryIntentFromList(Intent intent, String resolvedType, + boolean defaultOnly, ArrayList<ArrayList<F>> listCut) { + ArrayList<R> resultList = new ArrayList<R>(); + + final boolean debug = localLOGV || + ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); + + final String scheme = intent.getScheme(); + int N = listCut.size(); + for (int i = 0; i < N; ++i) { + buildResolveList(intent, debug, defaultOnly, + resolvedType, scheme, listCut.get(i), resultList); + } + sortResults(resultList); + return resultList; + } + + public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { String scheme = intent.getScheme(); ArrayList<R> finalList = new ArrayList<R>(); diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 05888e0..fab97b1 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -46,7 +46,6 @@ import android.location.Address; import android.location.IGeocodeProvider; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; -import android.location.ILocationCollector; import android.location.ILocationListener; import android.location.ILocationManager; import android.location.ILocationProvider; @@ -66,15 +65,12 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; -import android.util.Config; import android.util.Log; import android.util.PrintWriterPrinter; -import android.util.SparseIntArray; import com.android.internal.location.GpsLocationProvider; import com.android.internal.location.LocationProviderProxy; import com.android.internal.location.MockProvider; -import com.android.server.am.BatteryStatsService; /** * The service class that manages LocationProviders and issues location @@ -107,8 +103,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS; private static final String INSTALL_LOCATION_PROVIDER = android.Manifest.permission.INSTALL_LOCATION_PROVIDER; - private static final String INSTALL_LOCATION_COLLECTOR = - android.Manifest.permission.INSTALL_LOCATION_COLLECTOR; // Set of providers that are explicitly enabled private final Set<String> mEnabledProviders = new HashSet<String>(); @@ -171,9 +165,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private HashMap<String,Location> mLastKnownLocation = new HashMap<String,Location>(); - // Location collector - private ILocationCollector mCollector; - private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE; // for Settings change notification @@ -516,6 +507,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private void removeProvider(LocationProviderProxy provider) { mProviders.remove(provider); + provider.unlinkProvider(); mProvidersByName.remove(provider.getName()); } @@ -630,16 +622,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - public void installLocationCollector(ILocationCollector collector) { - if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_COLLECTOR) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires INSTALL_LOCATION_COLLECTOR permission"); - } - - // FIXME - only support one collector - mCollector = collector; - } - public void installGeocodeProvider(IGeocodeProvider provider) { if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER) != PackageManager.PERMISSION_GRANTED) { @@ -666,14 +648,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private void checkPermissionsSafe(String provider) { if (LocationManager.GPS_PROVIDER.equals(provider) - && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); } if (LocationManager.NETWORK_PROVIDER.equals(provider) - && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) - && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException( "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); @@ -682,14 +664,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private boolean isAllowedProviderSafe(String provider) { if (LocationManager.GPS_PROVIDER.equals(provider) - && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { return false; } if (LocationManager.NETWORK_PROVIDER.equals(provider) - && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) - && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { return false; } @@ -788,8 +770,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) { if (deadReceivers == null) { deadReceivers = new ArrayList<Receiver>(); - deadReceivers.add(record.mReceiver); } + deadReceivers.add(record.mReceiver); } listeners++; } @@ -1093,7 +1075,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (mGpsStatusProvider == null) { return false; } - if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) != + if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); } @@ -1121,7 +1103,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // first check for permission to the provider checkPermissionsSafe(provider); // and check for ACCESS_LOCATION_EXTRA_COMMANDS - if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS) + if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS) != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission"); } @@ -1619,23 +1601,19 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (mLock) { Location location = (Location) msg.obj; + String provider = location.getProvider(); - if (mCollector != null && - LocationManager.GPS_PROVIDER.equals(location.getProvider())) { - try { - mCollector.updateLocation(location); - } catch (RemoteException e) { - Log.w(TAG, "mCollector.updateLocation failed"); - mCollector = null; + // 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); } } - String provider = location.getProvider(); - if (!isAllowedBySettingsLocked(provider)) { - return; + if (isAllowedBySettingsLocked(provider)) { + handleLocationChangedLocked(location); } - - handleLocationChangedLocked(location); } } } catch (Exception e) { @@ -1935,7 +1913,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (mLock) { pw.println("Current Location Manager state:"); pw.println(" sProvidersLoaded=" + sProvidersLoaded); - pw.println(" mCollector=" + mCollector); pw.println(" Listeners:"); int N = mReceivers.size(); for (int i=0; i<N; i++) { diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 6ed8b4c..854138c 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -16,47 +16,52 @@ package com.android.server; +import com.android.server.status.IconData; +import com.android.server.status.NotificationData; +import com.android.server.status.StatusBarService; + import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.INotificationManager; import android.app.ITransientNotification; import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentQueryMap; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; -import android.media.AudioManager; +import android.database.ContentObserver; import android.media.AsyncPlayer; -import android.media.RingtoneManager; +import android.media.AudioManager; import android.net.Uri; import android.os.BatteryManager; import android.os.Binder; -import android.os.RemoteException; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Power; +import android.os.RemoteException; import android.os.Vibrator; import android.provider.Settings; -import android.util.Config; +import android.text.TextUtils; import android.util.EventLog; import android.util.Log; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.Toast; -import com.android.server.status.IconData; -import com.android.server.status.NotificationData; -import com.android.server.status.StatusBarService; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; -import java.io.IOException; class NotificationManagerService extends INotificationManager.Stub { @@ -88,6 +93,12 @@ class NotificationManagerService extends INotificationManager.Stub private NotificationRecord mVibrateNotification; private Vibrator mVibrator = new Vibrator(); + // adb + private int mBatteryPlugged; + private boolean mAdbEnabled = false; + private boolean mAdbNotificationShown = false; + private Notification mAdbNotification; + private ArrayList<NotificationRecord> mNotificationList; private ArrayList<ToastRecord> mToastQueue; @@ -98,7 +109,7 @@ class NotificationManagerService extends INotificationManager.Stub private boolean mBatteryLow; private boolean mBatteryFull; private NotificationRecord mLedNotification; - + private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on @@ -297,6 +308,9 @@ class NotificationManagerService extends INotificationManager.Stub mBatteryFull = batteryFull; updateLights(); } + + mBatteryPlugged = intent.getIntExtra("plugged", 0); + updateAdbNotification(); } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) { Uri uri = intent.getData(); @@ -312,6 +326,31 @@ class NotificationManagerService extends INotificationManager.Stub } }; + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.ADB_ENABLED), false, this); + update(); + } + + @Override public void onChange(boolean selfChange) { + update(); + } + + public void update() { + ContentResolver resolver = mContext.getContentResolver(); + mAdbEnabled = Settings.Secure.getInt(resolver, + Settings.Secure.ADB_ENABLED, 0) != 0; + updateAdbNotification(); + } + } + private final SettingsObserver mSettingsObserver; + NotificationManagerService(Context context, StatusBarService statusBar, HardwareService hardware) { @@ -333,6 +372,9 @@ class NotificationManagerService extends INotificationManager.Stub filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); mContext.registerReceiver(mIntentReceiver, filter); + + mSettingsObserver = new SettingsObserver(mHandler); + mSettingsObserver.observe(); } // Toasts @@ -594,6 +636,9 @@ class NotificationManagerService extends INotificationManager.Stub Binder.restoreCallingIdentity(identity); } } + + sendAccessibilityEventTypeNotificationChangedDoCheck(notification, pkg); + } else { if (old != null && old.statusBarKey != null) { long identity = Binder.clearCallingIdentity(); @@ -676,6 +721,26 @@ class NotificationManagerService extends INotificationManager.Stub idOut[0] = id; } + private void sendAccessibilityEventTypeNotificationChangedDoCheck(Notification notification, + CharSequence packageName) { + AccessibilityManager manager = AccessibilityManager.getInstance(mContext); + if (!manager.isEnabled()) { + return; + } + + AccessibilityEvent event = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); + event.setPackageName(packageName); + event.setClassName(Notification.class.getName()); + event.setParcelableData(notification); + CharSequence tickerText = notification.tickerText; + if (!TextUtils.isEmpty(tickerText)) { + event.getText().add(tickerText); + } + + manager.sendAccessibilityEvent(event); + } + private void cancelNotificationLocked(NotificationRecord r) { // status bar if (r.notification.icon != 0) { @@ -869,6 +934,62 @@ class NotificationManagerService extends INotificationManager.Stub return -1; } + // This is here instead of StatusBarPolicy because it is an important + // security feature that we don't want people customizing the platform + // to accidentally lose. + private void updateAdbNotification() { + if (mAdbEnabled && mBatteryPlugged == BatteryManager.BATTERY_PLUGGED_USB) { + if (!mAdbNotificationShown) { + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) { + Resources r = mContext.getResources(); + CharSequence title = r.getText( + com.android.internal.R.string.adb_active_notification_title); + CharSequence message = r.getText( + com.android.internal.R.string.adb_active_notification_message); + + if (mAdbNotification == null) { + mAdbNotification = new Notification(); + mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning; + mAdbNotification.when = 0; + mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT; + mAdbNotification.tickerText = title; + mAdbNotification.defaults |= Notification.DEFAULT_SOUND; + } + + Intent intent = new Intent( + Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + // Note: we are hard-coding the component because this is + // an important security UI that we don't want anyone + // intercepting. + intent.setComponent(new ComponentName("com.android.settings", + "com.android.settings.DevelopmentSettings")); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, + 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); + if (notificationManager != null) { + mAdbNotificationShown = false; + notificationManager.cancel( + com.android.internal.R.string.adb_active_notification_title); + } + } + } + // ====================================================================== @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java new file mode 100644 index 0000000..786f423 --- /dev/null +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.BackupAgent; +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.os.Build; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +/** + * We back up the signatures of each package so that during a system restore, + * we can verify that the app whose data we think we have matches the app + * actually resident on the device. + * + * Since the Package Manager isn't a proper "application" we just provide a + * direct IBackupAgent implementation and hand-construct it at need. + */ +public class PackageManagerBackupAgent extends BackupAgent { + private static final String TAG = "PMBA"; + private static final boolean DEBUG = true; + + // key under which we store global metadata (individual app metadata + // is stored using the package name as a key) + private static final String GLOBAL_METADATA_KEY = "@meta@"; + + private List<PackageInfo> mAllPackages; + private PackageManager mPackageManager; + // version & signature info of each app in a restore set + private HashMap<String, Metadata> mRestoredSignatures; + // The version info of each backed-up app as read from the state file + private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>(); + + private final HashSet<String> mExisting = new HashSet<String>(); + private int mStoredSdkVersion; + private String mStoredIncrementalVersion; + private boolean mHasMetadata; + + public class Metadata { + public int versionCode; + public Signature[] signatures; + + Metadata(int version, Signature[] sigs) { + versionCode = version; + signatures = sigs; + } + } + + // We're constructed with the set of applications that are participating + // in backup. This set changes as apps are installed & removed. + PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) { + mPackageManager = packageMgr; + mAllPackages = packages; + mRestoredSignatures = null; + mHasMetadata = false; + } + + public boolean hasMetadata() { + return mHasMetadata; + } + + public Metadata getRestoredMetadata(String packageName) { + if (mRestoredSignatures == null) { + Log.w(TAG, "getRestoredMetadata() before metadata read!"); + return null; + } + + return mRestoredSignatures.get(packageName); + } + + // 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()"); + + ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these + DataOutputStream outWriter = new DataOutputStream(bufStream); + parseStateFile(oldState); + + // If the stored version string differs, we need to re-backup all + // of the metadata. We force this by removing everything from the + // "already backed up" map built by parseStateFile(). + if (mStoredIncrementalVersion == null + || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) { + Log.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs " + + Build.VERSION.INCREMENTAL + " - rewriting"); + mExisting.clear(); + } + + try { + /* + * Global metadata: + * + * int SDKversion -- the SDK version of the OS itself on the device + * that produced this backup set. Used to reject + * backups from later OSes onto earlier ones. + * String incremental -- the incremental release name of the OS stored in + * 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); + } else { + if (DEBUG) Log.v(TAG, "Global metadata key already stored"); + // don't consider it to have been skipped/deleted + mExisting.remove(GLOBAL_METADATA_KEY); + } + + // For each app we have on device, see if we've backed it up yet. If not, + // write its signature block to the output, keyed on the package name. + for (PackageInfo pkg : mAllPackages) { + String packName = pkg.packageName; + if (packName.equals(GLOBAL_METADATA_KEY)) { + // We've already handled the metadata key; skip it here + continue; + } else { + PackageInfo info = null; + try { + info = mPackageManager.getPackageInfo(packName, + PackageManager.GET_SIGNATURES); + } catch (NameNotFoundException e) { + // Weird; we just found it, and now are told it doesn't exist. + // Treat it as having been removed from the device. + mExisting.add(packName); + 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 + // 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 (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); + + // !!! TODO: take out this debugging + 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); + } + } + } + + // At this point, the only entries in 'existing' are apps that were + // mentioned in the saved state file, but appear to no longer be present + // on the device. Write a deletion entity for them. + for (String app : mExisting) { + // !!! TODO: take out this msg + if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app); + try { + data.writeEntityHeader(app, -1); + } catch (IOException e) { + Log.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!"); + return; + } + + // Finally, write the new state blob -- just the list of all apps we handled + writeStateFile(mAllPackages, newState); + } + + // "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 + // image. We'll use those later to determine what we can legitimately restore. + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) + throws IOException { + List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); + HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>(); + if (DEBUG) Log.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); + + // 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); + + if (key.equals(GLOBAL_METADATA_KEY)) { + int storedSdkVersion = in.readInt(); + if (DEBUG) Log.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"); + return; + } + mStoredSdkVersion = storedSdkVersion; + mStoredIncrementalVersion = in.readUTF(); + mHasMetadata = true; + // !!! TODO: remove this debugging output + if (DEBUG) { + Log.i(TAG, "Restore set version " + storedSystemVersion + + " is compatible with OS version " + Build.VERSION.SDK_INT + + " (" + mStoredIncrementalVersion + " vs " + + Build.VERSION.INCREMENTAL + ")"); + } + } else { + // it's a file metadata record + int versionCode = in.readInt(); + Signature[] sigs = unflattenSignatureArray(in); +// !!! TODO: take out this debugging + if (DEBUG) { + Log.i(TAG, " restored metadata for " + key + + " dataSize=" + dataSize + + " versionCode=" + versionCode + " sigs=" + sigs); + } + + ApplicationInfo app = new ApplicationInfo(); + app.packageName = key; + restoredApps.add(app); + sigMap.put(key, new Metadata(versionCode, sigs)); + } + } + + // On successful completion, cache the signature map for the Backup Manager to use + 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; + } + + return outBuf.toByteArray(); + } + + private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) { + Signature[] sigs = null; + + try { + int num = in.readInt(); + Log.v(TAG, " ... unflatten read " + num); + 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"); + } + } catch (IOException e) { + Log.d(TAG, "Unable to unflatten sigs"); + return null; + } + + return sigs; + } + + // Util: parse out an existing state file into a usable structure + private void parseStateFile(ParcelFileDescriptor stateFile) { + mExisting.clear(); + mStateVersions.clear(); + mStoredSdkVersion = 0; + mStoredIncrementalVersion = null; + + // The state file is just the list of app names we have stored signatures for + // with the exception of the metadata block, to which is also appended the + // version numbers corresponding with the last time we wrote this PM block. + // If they mismatch the current system, we'll re-store the metadata key. + FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor()); + DataInputStream in = new DataInputStream(instream); + + int bufSize = 256; + byte[] buf = new byte[bufSize]; + try { + String pkg = in.readUTF(); + if (pkg.equals(GLOBAL_METADATA_KEY)) { + mStoredSdkVersion = in.readInt(); + mStoredIncrementalVersion = in.readUTF(); + mExisting.add(GLOBAL_METADATA_KEY); + } else { + Log.e(TAG, "No global metadata in state file!"); + return; + } + + // The global metadata was first; now read all the apps + while (true) { + pkg = in.readUTF(); + int versionCode = in.readInt(); + mExisting.add(pkg); + mStateVersions.put(pkg, new Metadata(versionCode, null)); + } + } catch (EOFException eof) { + // safe; we're done + } catch (IOException e) { + // whoops, bad state file. abort. + Log.e(TAG, "Unable to read Package Manager state file: " + e); + } + } + + // Util: write out our new backup state file + private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) { + FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); + DataOutputStream out = new DataOutputStream(outstream); + + try { + // by the time we get here we know we've stored the global metadata record + out.writeUTF(GLOBAL_METADATA_KEY); + out.writeInt(Build.VERSION.SDK_INT); + out.writeUTF(Build.VERSION.INCREMENTAL); + + // now write all the app names too + for (PackageInfo pkg : pkgs) { + out.writeUTF(pkg.packageName); + out.writeInt(pkg.versionCode); + } + } catch (IOException e) { + Log.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 079f363..06435c8 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -26,13 +26,12 @@ import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManagerNative; import android.app.IActivityManager; -import android.app.PendingIntent; -import android.app.PendingIntent.CanceledException; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.IntentSender.SendIntentException; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; @@ -57,6 +56,8 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -147,6 +148,7 @@ class PackageManagerService extends IPackageManager.Stub { final Context mContext; final boolean mFactoryTest; + final boolean mNoDexOpt; final DisplayMetrics mMetrics; final int mDefParseFlags; final String[] mSeparateProcesses; @@ -258,6 +260,7 @@ class PackageManagerService extends IPackageManager.Stub { final ResolveInfo mResolveInfo = new ResolveInfo(); ComponentName mResolveComponentName; PackageParser.Package mPlatformPackage; + private boolean mCompatibilityModeEnabled = true; public static final IPackageManager main(Context context, boolean factoryTest) { PackageManagerService m = new PackageManagerService(context, factoryTest); @@ -297,6 +300,7 @@ class PackageManagerService extends IPackageManager.Stub { mContext = context; mFactoryTest = factoryTest; + mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics(); mSettings = new Settings(); mSettings.addSharedUserLP("android.uid.system", @@ -366,6 +370,10 @@ class PackageManagerService extends IPackageManager.Stub { startTime); int scanMode = SCAN_MONITOR; + if (mNoDexOpt) { + Log.w(TAG, "Running ENG build: no pre-dexopt!"); + scanMode |= SCAN_NO_DEX; + } final HashSet<String> libFiles = new HashSet<String>(); @@ -508,7 +516,7 @@ class PackageManagerService extends IPackageManager.Stub { } // synchronized (mPackages) } // synchronized (mInstallLock) } - + @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { @@ -884,7 +892,11 @@ class PackageManagerService extends IPackageManager.Stub { + ": " + p); if (p != null) { // Note: isEnabledLP() does not apply here - always return info - return PackageParser.generateApplicationInfo(p, flags); + ApplicationInfo appInfo = PackageParser.generateApplicationInfo(p, flags); + if (!mCompatibilityModeEnabled) { + appInfo.disableCompatibilityMode(); + } + return appInfo; } if ("android".equals(packageName)||"system".equals(packageName)) { return mAndroidApplication; @@ -922,7 +934,7 @@ class PackageManagerService extends IPackageManager.Stub { }); } - public void freeStorage(final long freeStorageSize, final PendingIntent opFinishedIntent) { + public void freeStorage(final long freeStorageSize, final IntentSender pi) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); // Queue up an async operation since clearing cache may take a little while. @@ -936,11 +948,13 @@ class PackageManagerService extends IPackageManager.Stub { Log.w(TAG, "Couldn't clear application caches"); } } - if(opFinishedIntent != null) { + if(pi != null) { try { // Callback via pending intent - opFinishedIntent.send((retCode >= 0) ? 1 : 0); - } catch (CanceledException e1) { + int code = (retCode >= 0) ? 1 : 0; + pi.sendIntent(null, code, null, + null, null); + } catch (SendIntentException e1) { Log.i(TAG, "Failed to send pending intent"); } } @@ -951,8 +965,8 @@ class PackageManagerService extends IPackageManager.Stub { public ActivityInfo getActivityInfo(ComponentName component, int flags) { synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); - if (Config.LOGV) Log.v( - TAG, "getActivityInfo " + component + ": " + a); + + if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledLP(a.info, flags)) { return PackageParser.generateActivityInfo(a, flags); } @@ -1203,6 +1217,11 @@ class PackageManagerService extends IPackageManager.Stub { public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags) { List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags); + return chooseBestActivity(intent, resolvedType, flags, query); + } + + private ResolveInfo chooseBestActivity(Intent intent, String resolvedType, + int flags, List<ResolveInfo> query) { if (query != null) { final int N = query.size(); if (N == 1) { @@ -1243,8 +1262,7 @@ class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); List<PreferredActivity> prefs = - mSettings.mPreferredActivities.queryIntent(null, - intent, resolvedType, + mSettings.mPreferredActivities.queryIntent(intent, resolvedType, (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); if (prefs != null && prefs.size() > 0) { // First figure out how good the original match set is. @@ -1320,8 +1338,17 @@ class PackageManagerService extends IPackageManager.Stub { } synchronized (mPackages) { - return (List<ResolveInfo>)mActivities. - queryIntent(null, intent, resolvedType, flags); + String pkgName = intent.getPackage(); + if (pkgName == null) { + return (List<ResolveInfo>)mActivities.queryIntent(intent, + resolvedType, flags); + } + PackageParser.Package pkg = mPackages.get(pkgName); + if (pkg != null) { + return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent, + resolvedType, flags, pkg.activities); + } + return null; } } @@ -1488,9 +1515,30 @@ class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) { + ComponentName comp = intent.getComponent(); + if (comp != null) { + List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); + ActivityInfo ai = getReceiverInfo(comp, flags); + if (ai != null) { + ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = ai; + list.add(ri); + } + return list; + } + synchronized (mPackages) { - return (List<ResolveInfo>)mReceivers. - queryIntent(null, intent, resolvedType, flags); + String pkgName = intent.getPackage(); + if (pkgName == null) { + return (List<ResolveInfo>)mReceivers.queryIntent(intent, + resolvedType, flags); + } + PackageParser.Package pkg = mPackages.get(pkgName); + if (pkg != null) { + return (List<ResolveInfo>) mReceivers.queryIntentForPackage(intent, + resolvedType, flags, pkg.receivers); + } + return null; } } @@ -1523,8 +1571,17 @@ class PackageManagerService extends IPackageManager.Stub { } synchronized (mPackages) { - return (List<ResolveInfo>)mServices. - queryIntent(null, intent, resolvedType, flags); + String pkgName = intent.getPackage(); + if (pkgName == null) { + return (List<ResolveInfo>)mServices.queryIntent(intent, + resolvedType, flags); + } + PackageParser.Package pkg = mPackages.get(pkgName); + if (pkg != null) { + return (List<ResolveInfo>)mServices.queryIntentForPackage(intent, + resolvedType, flags, pkg.services); + } + return null; } } @@ -1837,7 +1894,56 @@ class PackageManagerService extends IPackageManager.Stub { } return true; } + + public boolean performDexOpt(String packageName) { + if (!mNoDexOpt) { + return false; + } + PackageParser.Package p; + synchronized (mPackages) { + p = mPackages.get(packageName); + if (p == null || p.mDidDexOpt) { + return false; + } + } + synchronized (mInstallLock) { + 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) { + String path = pkg.mScanPath; + int ret = 0; + try { + if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { + ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, + !pkg.mForwardLocked); + pkg.mDidDexOpt = true; + performed = true; + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Apk not found for dexopt: " + path); + ret = -1; + } catch (IOException e) { + Log.w(TAG, "Exception reading apk: " + path, e); + ret = -1; + } + if (ret < 0) { + //error from installer + 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) { @@ -2126,37 +2232,18 @@ class PackageManagerService extends IPackageManager.Stub { String path = scanFile.getPath(); if (scanFileNewer) { Log.i(TAG, path + " changed; unpacking"); - try { - cachePackageSharedLibsLI(pkg, dataPath, scanFile); - } catch (IOException e) { - Log.e(TAG, "Failure extracting shared libs", e); - if(mInstaller != null) { - mInstaller.remove(pkgName); - } else { - dataPath.delete(); - } - mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + int err = cachePackageSharedLibsLI(pkg, dataPath, scanFile); + if (err != PackageManager.INSTALL_SUCCEEDED) { + mLastScanError = err; return null; } } - if ((scanMode&SCAN_NO_DEX) == 0 - && (pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - int ret = 0; - try { - if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { - ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, - (scanMode&SCAN_FORWARD_LOCKED) == 0); - } - } catch (FileNotFoundException e) { - Log.w(TAG, "Apk not found for dexopt: " + path); - ret = -1; - } catch (IOException e) { - Log.w(TAG, "Exception reading apk: " + path, e); - ret = -1; - } - if (ret < 0) { - //error from installer + 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; return null; } @@ -2419,14 +2506,15 @@ class PackageManagerService extends IPackageManager.Stub { return pkg; } - private void cachePackageSharedLibsLI(PackageParser.Package pkg, - File dataPath, File scanFile) throws IOException { + private int cachePackageSharedLibsLI(PackageParser.Package pkg, + File dataPath, File scanFile) { File sharedLibraryDir = new File(dataPath.getPath() + "/lib"); - final String sharedLibraryABI = "armeabi"; + final String sharedLibraryABI = Build.CPU_ABI; final String apkLibraryDirectory = "lib/" + sharedLibraryABI + "/"; final String apkSharedLibraryPrefix = apkLibraryDirectory + "lib"; final String sharedLibrarySuffix = ".so"; - boolean createdSharedLib = false; + boolean hasNativeCode = false; + boolean installedNativeCode = false; try { ZipFile zipFile = new ZipFile(scanFile); Enumeration<ZipEntry> entries = @@ -2435,9 +2523,15 @@ class PackageManagerService extends IPackageManager.Stub { while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (entry.isDirectory()) { + if (!hasNativeCode && entry.getName().startsWith("lib")) { + hasNativeCode = true; + } continue; } String entryName = entry.getName(); + if (entryName.startsWith("lib/")) { + hasNativeCode = true; + } if (! (entryName.startsWith(apkSharedLibraryPrefix) && entryName.endsWith(sharedLibrarySuffix))) { continue; @@ -2448,6 +2542,9 @@ class PackageManagerService extends IPackageManager.Stub { || (!FileUtils.isFilenameSafe(new File(libFileName)))) { continue; } + + installedNativeCode = true; + String sharedLibraryFilePath = sharedLibraryDir.getPath() + File.separator + libFileName; File sharedLibraryFile = new File(sharedLibraryFilePath); @@ -2459,19 +2556,23 @@ class PackageManagerService extends IPackageManager.Stub { } if (mInstaller == null) { sharedLibraryDir.mkdir(); - createdSharedLib = true; } cacheSharedLibLI(pkg, zipFile, entry, sharedLibraryDir, sharedLibraryFile); } } } catch (IOException e) { - Log.e(TAG, "Failed to cache package shared libs", e); - if(createdSharedLib) { - sharedLibraryDir.delete(); - } - throw e; + Log.w(TAG, "Failed to cache package shared libs", e); + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } + + if (hasNativeCode && !installedNativeCode) { + Log.w(TAG, "Install failed: .apk has native code but none for arch " + + Build.CPU_ABI); + return PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE; + } + + return PackageManager.INSTALL_SUCCEEDED; } private void cacheSharedLibLI(PackageParser.Package pkg, @@ -2801,6 +2902,21 @@ class PackageManagerService extends IPackageManager.Stub { // we can't add any new permissions to it. if (!gp.loadedPermissions.contains(perm)) { allowed = false; + // Except... if this is a permission that was added + // to the platform (note: need to only do this when + // updating the platform). + final int NP = PackageParser.NEW_PERMISSIONS.length; + for (int ip=0; ip<NP; ip++) { + final PackageParser.NewPermissionInfo npi + = PackageParser.NEW_PERMISSIONS[ip]; + if (npi.name.equals(perm) + && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) { + allowed = true; + Log.i(TAG, "Auto-granting WRITE_EXTERNAL_STORAGE to old pkg " + + pkg.packageName); + break; + } + } } } if (allowed) { @@ -2839,20 +2955,38 @@ class PackageManagerService extends IPackageManager.Stub { private final class ActivityIntentResolver extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> { - public List queryIntent(ContentResolver resolver, Intent intent, - String resolvedType, boolean defaultOnly) { + public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; - return super.queryIntent(resolver, intent, resolvedType, defaultOnly); + return super.queryIntent(intent, resolvedType, defaultOnly); } - public List queryIntent(ContentResolver resolver, Intent intent, - String resolvedType, int flags) { + public List queryIntent(Intent intent, String resolvedType, int flags) { mFlags = flags; - return super.queryIntent( - resolver, intent, resolvedType, + return super.queryIntent(intent, resolvedType, (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); } + public List queryIntentForPackage(Intent intent, String resolvedType, int flags, + ArrayList<PackageParser.Activity> packageActivities) { + if (packageActivities == null) { + return null; + } + mFlags = flags; + final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; + int N = packageActivities.size(); + ArrayList<ArrayList<PackageParser.ActivityIntentInfo>> listCut = + new ArrayList<ArrayList<PackageParser.ActivityIntentInfo>>(N); + + ArrayList<PackageParser.ActivityIntentInfo> intentFilters; + for (int i = 0; i < N; ++i) { + intentFilters = packageActivities.get(i).intents; + if (intentFilters != null && intentFilters.size() > 0) { + listCut.add(intentFilters); + } + } + return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut); + } + public final void addActivity(PackageParser.Activity a, String type) { mActivities.put(a.component, a); if (SHOW_INFO || Config.LOGV) Log.v( @@ -2860,8 +2994,7 @@ class PackageManagerService extends IPackageManager.Stub { (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); if (SHOW_INFO || Config.LOGV) Log.v(TAG, " Class=" + a.info.name); int NI = a.intents.size(); - int j; - for (j=0; j<NI; j++) { + for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (SHOW_INFO || Config.LOGV) { Log.v(TAG, " IntentFilter:"); @@ -2881,8 +3014,7 @@ class PackageManagerService extends IPackageManager.Stub { (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); if (SHOW_INFO || Config.LOGV) Log.v(TAG, " Class=" + a.info.name); int NI = a.intents.size(); - int j; - for (j=0; j<NI; j++) { + for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (SHOW_INFO || Config.LOGV) { Log.v(TAG, " IntentFilter:"); @@ -2969,20 +3101,38 @@ class PackageManagerService extends IPackageManager.Stub { private final class ServiceIntentResolver extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> { - public List queryIntent(ContentResolver resolver, Intent intent, - String resolvedType, boolean defaultOnly) { + public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; - return super.queryIntent(resolver, intent, resolvedType, defaultOnly); + return super.queryIntent(intent, resolvedType, defaultOnly); } - public List queryIntent(ContentResolver resolver, Intent intent, - String resolvedType, int flags) { + public List queryIntent(Intent intent, String resolvedType, int flags) { mFlags = flags; - return super.queryIntent( - resolver, intent, resolvedType, + return super.queryIntent(intent, resolvedType, (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); } + public List queryIntentForPackage(Intent intent, String resolvedType, int flags, + ArrayList<PackageParser.Service> packageServices) { + if (packageServices == null) { + return null; + } + mFlags = flags; + final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; + int N = packageServices.size(); + ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut = + new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N); + + ArrayList<PackageParser.ServiceIntentInfo> intentFilters; + for (int i = 0; i < N; ++i) { + intentFilters = packageServices.get(i).intents; + if (intentFilters != null && intentFilters.size() > 0) { + listCut.add(intentFilters); + } + } + return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut); + } + public final void addService(PackageParser.Service s) { mServices.put(s.component, s); if (SHOW_INFO || Config.LOGV) Log.v( @@ -3142,6 +3292,7 @@ class PackageManagerService extends IPackageManager.Stub { if (extras != null) { intent.putExtras(extras); } + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); am.broadcastIntent( null, intent, null, null, 0, null, null, null, false, false); @@ -3545,7 +3696,9 @@ class PackageManagerService extends IPackageManager.Stub { } else { // Re installation failed. Restore old information // Remove new pkg information - removePackageLI(newPackage, true); + if (newPackage != null) { + removePackageLI(newPackage, true); + } // Add back the old system package scanPackageLI(oldPkgSetting.codePath, oldPkgSetting.codePath, oldPkgSetting.resourcePath, @@ -4084,6 +4237,7 @@ class PackageManagerService extends IPackageManager.Stub { return false; } synchronized (mPackages) { + grantPermissionsLP(newPkg, true); mSettings.writeLP(); } return true; @@ -4445,6 +4599,42 @@ class PackageManagerService extends IPackageManager.Stub { } } + 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."); + } + if (filter.countCategories() != 1) { + throw new IllegalArgumentException( + "replacePreferredActivity expects filter to have only 1 category."); + } + if (filter.countDataAuthorities() != 0 + || filter.countDataPaths() != 0 + || filter.countDataSchemes() != 0 + || filter.countDataTypes() != 0) { + throw new IllegalArgumentException( + "replacePreferredActivity expects filter to have no data authorities, " + + "paths, schemes or types."); + } + synchronized (mPackages) { + Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); + String action = filter.getAction(0); + String category = filter.getCategory(0); + while (it.hasNext()) { + PreferredActivity pa = it.next(); + if (pa.getAction(0).equals(action) && pa.getCategory(0).equals(category)) { + it.remove(); + Log.i(TAG, "Removed preferred activity " + pa.mActivity + ":"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); + } + } + addPreferredActivity(filter, match, set, activity); + } + } + public void clearPackagePreferredActivities(String packageName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); @@ -4609,6 +4799,14 @@ class PackageManagerService extends IPackageManager.Stub { public void systemReady() { mSystemReady = true; + + // Read the compatibilty setting when the system is ready. + mCompatibilityModeEnabled = android.provider.Settings.System.getInt( + mContext.getContentResolver(), + android.provider.Settings.System.COMPATIBILITY_MODE, 1) == 1; + if (DEBUG_SETTINGS) { + Log.d(TAG, "compatibility mode:" + mCompatibilityModeEnabled); + } } public boolean isSafeMode() { @@ -5607,24 +5805,15 @@ class PackageManagerService extends IPackageManager.Stub { // Check to see if its a disabled system app PackageSetting ps = mDisabledSysPackages.get(name); if((ps != null) && ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0)) { - // Could be a replaced system package - // Note that if the user replaced a system app, the user has to physically - // delete the new one in order to revert to the system app. So even - // if the user updated the system app via an update, the user still - // has to delete the one installed in the data partition in order to pick up the - // new system package. + // This is an updated system app with versions in both system + // and data partition. Just let the most recent version + // take precedence. return p; - } else if ((p.pkg != null) && (p.pkg.applicationInfo != null) && - ((p.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) { - // Check for non-system apps + } else if ((p.pkg != null) && (p.pkg.applicationInfo != null)) { + // Let the app continue with previous uid if code path changes. reportSettingsProblem(Log.WARN, "Package " + name + " codePath changed from " + p.codePath + " to " + codePath + "; Retaining data and using new code"); - } else { - reportSettingsProblem(Log.WARN, - "Package " + name + " codePath changed from " + p.codePath - + " to " + codePath + "; replacing with new"); - p = null; } } else if (p.sharedUser != sharedUser) { reportSettingsProblem(Log.WARN, @@ -5709,7 +5898,7 @@ class PackageManagerService extends IPackageManager.Stub { continue; } for (PackageSetting pkg:sus.packages) { - if (pkg.grantedPermissions.contains (eachPerm)) { + if (pkg.pkg.requestedPermissions.contains(eachPerm)) { used = true; break; } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index c5ea5fa..79d78ad 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -709,7 +709,10 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage p.awakeOnSet = true; } } else { - mPokeLocks.remove(token); + PokeLock rLock = mPokeLocks.remove(token); + if (rLock != null) { + token.unlinkToDeath(rLock, 0); + } } int oldPokey = mPokey; diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java index 55adabb..58f8980 100644 --- a/services/java/com/android/server/ProcessStats.java +++ b/services/java/com/android/server/ProcessStats.java @@ -54,7 +54,10 @@ public class ProcessStats { PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime }; + /** Stores user time and system time in 100ths of a second. */ private final long[] mProcessStatsData = new long[2]; + /** Stores user time and system time in 100ths of a second. */ + private final long[] mSinglePidStatsData = new long[2]; private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] { PROC_SPACE_TERM, @@ -418,7 +421,18 @@ public class ProcessStats { return pids; } - + + public long getCpuTimeForPid(int pid) { + final String statFile = "/proc/" + pid + "/stat"; + final long[] statsData = mSinglePidStatsData; + if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT, + null, statsData, null)) { + long time = statsData[0] + statsData[1]; + return time; + } + return 0; + } + final public int getLastUserTime() { return mRelUserTime; } diff --git a/services/java/com/android/server/RandomBlock.java b/services/java/com/android/server/RandomBlock.java new file mode 100644 index 0000000..4ac1c6e --- /dev/null +++ b/services/java/com/android/server/RandomBlock.java @@ -0,0 +1,100 @@ +/* + * 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.util.Log; + +import java.io.Closeable; +import java.io.DataOutput; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; + +/** + * A 4k block of random {@code byte}s. + */ +class RandomBlock { + + private static final String TAG = "RandomBlock"; + private static final int BLOCK_SIZE = 4096; + private byte[] block = new byte[BLOCK_SIZE]; + + private RandomBlock() { } + + static RandomBlock fromFile(String filename) throws IOException { + Log.v(TAG, "reading from file " + filename); + InputStream stream = null; + try { + stream = new FileInputStream(filename); + return fromStream(stream); + } finally { + close(stream); + } + } + + private static RandomBlock fromStream(InputStream in) throws IOException { + RandomBlock retval = new RandomBlock(); + int total = 0; + while(total < BLOCK_SIZE) { + int result = in.read(retval.block, total, BLOCK_SIZE - total); + if (result == -1) { + throw new EOFException(); + } + total += result; + } + return retval; + } + + void toFile(String filename) throws IOException { + Log.v(TAG, "writing to file " + filename); + RandomAccessFile out = null; + try { + out = new RandomAccessFile(filename, "rws"); + toDataOut(out); + truncateIfPossible(out); + } finally { + close(out); + } + } + + private static void truncateIfPossible(RandomAccessFile f) { + try { + f.setLength(BLOCK_SIZE); + } catch (IOException e) { + // ignore this exception. Sometimes, the file we're trying to + // write is a character device, such as /dev/urandom, and + // these character devices do not support setting the length. + } + } + + private void toDataOut(DataOutput out) throws IOException { + out.write(block); + } + + private static void close(Closeable c) { + try { + if (c == null) { + return; + } + c.close(); + } catch (IOException e) { + Log.w(TAG, "IOException thrown while closing Closeable", e); + } + } +} diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java index b253038..ceef39f 100644 --- a/services/java/com/android/server/SensorService.java +++ b/services/java/com/android/server/SensorService.java @@ -19,7 +19,7 @@ package com.android.server; import android.content.Context; import android.hardware.ISensorService; import android.os.Binder; -import android.os.ParcelFileDescriptor; +import android.os.Bundle; import android.os.RemoteException; import android.os.IBinder; import android.util.Config; @@ -101,7 +101,7 @@ class SensorService extends ISensorService.Stub { _sensors_control_init(); } - public ParcelFileDescriptor getDataChanel() throws RemoteException { + public Bundle getDataChannel() throws RemoteException { return _sensors_control_open(); } @@ -190,7 +190,7 @@ class SensorService extends ISensorService.Stub { ArrayList<Listener> mListeners = new ArrayList<Listener>(); private static native int _sensors_control_init(); - private static native ParcelFileDescriptor _sensors_control_open(); + private static native Bundle _sensors_control_open(); private static native boolean _sensors_control_activate(int sensor, boolean activate); private static native int _sensors_control_set_delay(int ms); private static native int _sensors_control_wake(); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 8b7260b..3e4d5f9 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -97,6 +97,9 @@ class ServerThread extends Thread { // Critical services... try { + Log.i(TAG, "Starting Entropy Service."); + ServiceManager.addService("entropy", new EntropyService()); + Log.i(TAG, "Starting Power Manager."); power = new PowerManagerService(); ServiceManager.addService(Context.POWER_SERVICE, power); @@ -228,6 +231,14 @@ class ServerThread extends Thread { } try { + Log.i(TAG, "Starting Accessibility Manager."); + ServiceManager.addService(Context.ACCESSIBILITY_SERVICE, + new AccessibilityManagerService(context)); + } catch (Throwable e) { + Log.e(TAG, "Failure starting Accessibility Manager", e); + } + + try { Log.i(TAG, "Starting Notification Manager."); ServiceManager.addService(Context.NOTIFICATION_SERVICE, new NotificationManagerService(context, statusBar, hardware)); diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index fa54421..9f2856c 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -26,6 +26,7 @@ import android.os.RemoteException; import android.telephony.CellLocation; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -39,48 +40,71 @@ import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.IPhoneStateListener; import com.android.internal.telephony.DefaultPhoneNotifier; import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneStateIntentReceiver; import com.android.internal.telephony.TelephonyIntents; import com.android.server.am.BatteryStatsService; - /** - * Since phone process can be restarted, this class provides a centralized - * place that applications can register and be called back from. + * Since phone process can be restarted, this class provides a centralized place + * that applications can register and be called back from. */ class TelephonyRegistry extends ITelephonyRegistry.Stub { private static final String TAG = "TelephonyRegistry"; private static class Record { String pkgForDebug; + IBinder binder; + IPhoneStateListener callback; + int events; } private final Context mContext; + private final ArrayList<Record> mRecords = new ArrayList(); + private final IBatteryStats mBatteryStats; private int mCallState = TelephonyManager.CALL_STATE_IDLE; + private String mCallIncomingNumber = ""; + private ServiceState mServiceState = new ServiceState(); - private int mSignalStrength = -1; + + private SignalStrength mSignalStrength = new SignalStrength(); + private boolean mMessageWaiting = false; + private boolean mCallForwarding = false; + private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; + private int mDataConnectionState = TelephonyManager.DATA_CONNECTED; + private boolean mDataConnectionPossible = false; + private String mDataConnectionReason = ""; + private String mDataConnectionApn = ""; + private String mDataConnectionInterfaceName = ""; + private Bundle mCellLocation = new Bundle(); - // we keep a copy of all of the sate so we can send it out when folks register for it + static final int PHONE_STATE_PERMISSION_MASK = + PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | + PhoneStateListener.LISTEN_CALL_STATE | + PhoneStateListener.LISTEN_DATA_ACTIVITY | + PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | + PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR; + + // we keep a copy of all of the state so we can send it out when folks + // register for it // - // In these calls we call with the lock held. This is safe becasuse remote - // calls go through a oneway interface and local calls going through a handler before - // they get to app code. + // In these calls we call with the lock held. This is safe becasuse remote + // calls go through a oneway interface and local calls going through a + // handler before they get to app code. TelephonyRegistry(Context context) { CellLocation.getEmpty().fillInNotifierBundle(mCellLocation); @@ -90,14 +114,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { public void listen(String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow) { - //Log.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + Integer.toHexString(events)); + // Log.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + + // Integer.toHexString(events)); if (events != 0) { - // check permissions - if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_COARSE_LOCATION, null); - - } + /* Checks permission and throws Security exception */ + checkListenerPermission(events); synchronized (mRecords) { // register @@ -105,7 +126,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { find_and_add: { IBinder b = callback.asBinder(); final int N = mRecords.size(); - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { r = mRecords.get(i); if (b == r.binder) { break find_and_add; @@ -125,7 +146,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { try { - r.callback.onSignalStrengthChanged(mSignalStrength); + int gsmSignalStrength = mSignalStrength.getGsmSignalStrength(); + r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 + : gsmSignalStrength)); } catch (RemoteException ex) { remove(r.binder); } @@ -168,6 +191,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { + try { + r.callback.onSignalStrengthsChanged(mSignalStrength); + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } else { @@ -177,8 +207,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private void remove(IBinder binder) { synchronized (mRecords) { - final int N = mRecords.size(); - for (int i=0; i<N; i++) { + final int recordCount = mRecords.size(); + for (int i = 0; i < recordCount; i++) { if (mRecords.get(i).binder == binder) { mRecords.remove(i); return; @@ -188,14 +218,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCallState(int state, String incomingNumber) { - if (!checkPhoneStatePermission("notifyCallState()")) { + if (!checkNotifyPermission("notifyCallState()")) { return; } synchronized (mRecords) { mCallState = state; mCallIncomingNumber = incomingNumber; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { try { @@ -210,13 +239,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyServiceState(ServiceState state) { - if (!checkPhoneStatePermission("notifyServiceState()")) { + if (!checkNotifyPermission("notifyServiceState()")){ return; - } + } synchronized (mRecords) { mServiceState = state; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { sendServiceState(r, state); @@ -226,35 +254,38 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { broadcastServiceStateChanged(state); } - public void notifySignalStrength(int signalStrengthASU) { - if (!checkPhoneStatePermission("notifySignalStrength()")) { + public void notifySignalStrength(SignalStrength signalStrength) { + if (!checkNotifyPermission("notifySignalStrength()")) { return; - } + } synchronized (mRecords) { - mSignalStrength = signalStrengthASU; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + mSignalStrength = signalStrength; + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); + if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { + sendSignalStrength(r, signalStrength); + } if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { try { - r.callback.onSignalStrengthChanged(signalStrengthASU); + int gsmSignalStrength = signalStrength.getGsmSignalStrength(); + r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 + : gsmSignalStrength)); } catch (RemoteException ex) { remove(r.binder); } } } } - broadcastSignalStrengthChanged(signalStrengthASU); + broadcastSignalStrengthChanged(signalStrength); } public void notifyMessageWaitingChanged(boolean mwi) { - if (!checkPhoneStatePermission("notifyMessageWaitingChanged()")) { + if (!checkNotifyPermission("notifyMessageWaitingChanged()")) { return; - } + } synchronized (mRecords) { mMessageWaiting = mwi; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { try { @@ -268,13 +299,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCallForwardingChanged(boolean cfi) { - if (!checkPhoneStatePermission("notifyCallForwardingChanged()")) { + if (!checkNotifyPermission("notifyCallForwardingChanged()")) { return; - } + } synchronized (mRecords) { mCallForwarding = cfi; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { try { @@ -288,13 +318,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyDataActivity(int state) { - if (!checkPhoneStatePermission("notifyDataActivity()")) { + if (!checkNotifyPermission("notifyDataActivity()" )) { return; - } + } synchronized (mRecords) { mDataActivity = state; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { try { @@ -307,19 +336,18 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - public void notifyDataConnection(int state, boolean isDataConnectivityPissible, + public void notifyDataConnection(int state, boolean isDataConnectivityPossible, String reason, String apn, String interfaceName) { - if (!checkPhoneStatePermission("notifyDataConnection()")) { + if (!checkNotifyPermission("notifyDataConnection()" )) { return; - } + } synchronized (mRecords) { mDataConnectionState = state; - mDataConnectionPossible = isDataConnectivityPissible; + mDataConnectionPossible = isDataConnectivityPossible; mDataConnectionReason = reason; mDataConnectionApn = apn; mDataConnectionInterfaceName = interfaceName; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { try { @@ -330,17 +358,17 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } } - broadcastDataConnectionStateChanged(state, isDataConnectivityPissible, - reason, apn, interfaceName); + broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn, + interfaceName); } public void notifyDataConnectionFailed(String reason) { - if (!checkPhoneStatePermission("notifyDataConnectionFailed()")) { + if (!checkNotifyPermission("notifyDataConnectionFailed()")) { return; - } + } /* * This is commented out because there is on onDataConnectionFailed callback - * on PhoneStateListener. There should be. + * on PhoneStateListener. There should be synchronized (mRecords) { mDataConnectionFailedReason = reason; final int N = mRecords.size(); @@ -356,13 +384,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCellLocation(Bundle cellLocation) { - if (!checkPhoneStatePermission("notifyCellLocation()")) { + if (!checkNotifyPermission("notifyCellLocation()")) { return; - } + } synchronized (mRecords) { mCellLocation = cellLocation; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { sendCellLocation(r, cellLocation); @@ -371,11 +398,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - // - // the new callback broadcasting - // - // copy the service state object so they can't mess it up in the local calls - // + /** + * Copy the service state object so they can't mess it up in the local calls + */ public void sendServiceState(Record r, ServiceState state) { try { r.callback.onServiceStateChanged(new ServiceState(state)); @@ -384,7 +409,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - public void sendCellLocation(Record r, Bundle cellLocation) { + private void sendCellLocation(Record r, Bundle cellLocation) { try { r.callback.onCellLocationChanged(new Bundle(cellLocation)); } catch (RemoteException ex) { @@ -392,18 +417,24 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + private void sendSignalStrength(Record r, SignalStrength signalStrength) { + try { + r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength)); + } catch (RemoteException ex) { + remove(r.binder); + } + } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump telephony.registry from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); return; } synchronized (mRecords) { - final int N = mRecords.size(); + final int recordCount = mRecords.size(); pw.println("last known state:"); pw.println(" mCallState=" + mCallState); pw.println(" mCallIncomingNumber=" + mCallIncomingNumber); @@ -418,20 +449,28 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println(" mDataConnectionApn=" + mDataConnectionApn); pw.println(" mDataConnectionInterfaceName=" + mDataConnectionInterfaceName); pw.println(" mCellLocation=" + mCellLocation); - pw.println("registrations: count=" + N); - for (int i=0; i<N; i++) { + pw.println("registrations: count=" + recordCount); + for (int i = 0; i < recordCount; i++) { Record r = mRecords.get(i); pw.println(" " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events)); } } } - // // the legacy intent broadcasting // private void broadcastServiceStateChanged(ServiceState state) { + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteAirplaneMode(state.getState() == ServiceState.STATE_POWER_OFF); + } catch (RemoteException re) { + // Can't do much + } finally { + Binder.restoreCallingIdentity(ident); + } + Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); Bundle data = new Bundle(); state.fillInNotifierBundle(data); @@ -439,17 +478,20 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mContext.sendStickyBroadcast(intent); } - private void broadcastSignalStrengthChanged(int asu) { + private void broadcastSignalStrengthChanged(SignalStrength signalStrength) { long ident = Binder.clearCallingIdentity(); try { - mBatteryStats.notePhoneSignalStrength(asu); + mBatteryStats.notePhoneSignalStrength(signalStrength); } catch (RemoteException e) { + /* The remote entity disappeared, we can safely ignore the exception. */ } finally { Binder.restoreCallingIdentity(ident); } - + Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED); - intent.putExtra(PhoneStateIntentReceiver.INTENT_KEY_ASU, asu); + Bundle data = new Bundle(); + signalStrength.fillInNotifierBundle(data); + intent.putExtras(data); mContext.sendStickyBroadcast(intent); } @@ -462,13 +504,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mBatteryStats.notePhoneOn(); } } catch (RemoteException e) { + /* The remote entity disappeared, we can safely ignore the exception. */ } finally { Binder.restoreCallingIdentity(ident); } - + Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED); - intent.putExtra(Phone.STATE_KEY, - DefaultPhoneNotifier.convertCallState(state).toString()); + intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString()); if (!TextUtils.isEmpty(incomingNumber)) { intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber); } @@ -498,16 +540,28 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { intent.putExtra(Phone.FAILURE_REASON_KEY, reason); mContext.sendStickyBroadcast(intent); } - - private boolean checkPhoneStatePermission(String method) { + + private boolean checkNotifyPermission(String method) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { return true; } String msg = "Modify Phone State Permission Denial: " + method + " from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid(); + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); Log.w(TAG, msg); return false; } + + private void checkListenerPermission(int events) { + if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_COARSE_LOCATION, null); + + } + + if ((events & PHONE_STATE_PERMISSION_MASK) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.READ_PHONE_STATE, null); + } + } } diff --git a/services/java/com/android/server/WallpaperService.java b/services/java/com/android/server/WallpaperService.java index 5532894..d921baf 100644 --- a/services/java/com/android/server/WallpaperService.java +++ b/services/java/com/android/server/WallpaperService.java @@ -18,8 +18,10 @@ package com.android.server; import static android.os.FileObserver.*; import static android.os.ParcelFileDescriptor.*; + import android.app.IWallpaperService; import android.app.IWallpaperServiceCallback; +import android.backup.BackupManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -154,7 +156,16 @@ class WallpaperService extends IWallpaperService.Stub { public ParcelFileDescriptor setWallpaper() { checkPermission(android.Manifest.permission.SET_WALLPAPER); try { - return ParcelFileDescriptor.open(WALLPAPER_FILE, MODE_CREATE|MODE_READ_WRITE); + ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE, + MODE_CREATE|MODE_READ_WRITE); + + // changing the wallpaper means we'll need to back up the new one + long origId = Binder.clearCallingIdentity(); + BackupManager bm = new BackupManager(mContext); + bm.dataChanged(); + Binder.restoreCallingIdentity(origId); + + return fd; } catch (FileNotFoundException e) { if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e); } diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index fef3598..68bf4fb 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -504,6 +504,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"); Process.killProcess(mPhonePid); } } else { @@ -848,6 +849,7 @@ public class Watchdog extends Thread { // Only kill the process if the debugger is not attached. if (!Debug.isDebuggerConnected()) { + Log.i(TAG, "Watchdog is killing the system process"); Process.killProcess(Process.myPid()); } } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 348f0a1..a940af3 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -38,6 +38,7 @@ import android.net.wifi.WifiNative; import android.net.wifi.WifiStateTracker; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; +import android.net.wifi.SupplicantState; import android.net.NetworkStateTracker; import android.net.DhcpInfo; import android.os.Binder; @@ -49,6 +50,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceManager; import android.provider.Settings; import android.util.Log; import android.text.TextUtils; @@ -64,6 +66,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import com.android.internal.app.IBatteryStats; +import android.backup.IBackupManager; import com.android.server.am.BatteryStatsService; /** @@ -96,8 +99,8 @@ public class WifiService extends IWifiManager.Stub { private int mScanLocksAcquired; private int mScanLocksReleased; - private final List<WifiMulticaster> mMulticasters = - new ArrayList<WifiMulticaster>(); + private final List<Multicaster> mMulticasters = + new ArrayList<Multicaster>(); private int mMulticastEnabled; private int mMulticastDisabled; @@ -588,6 +591,12 @@ public class WifiService extends IWifiManager.Stub { } + private void enforceMulticastChangePermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, + "WifiService"); + } + /** * see {@link WifiManager#getWifiState()} * @return One of {@link WifiManager#WIFI_STATE_DISABLED}, @@ -1054,6 +1063,94 @@ public class WifiService extends IWifiManager.Stub { break setVariables; } + if ((config.eap != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.eapVarName, + config.eap)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set eap: "+ + config.eap); + } + break setVariables; + } + + if ((config.identity != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.identityVarName, + config.identity)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set identity: "+ + config.identity); + } + break setVariables; + } + + if ((config.anonymousIdentity != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.anonymousIdentityVarName, + config.anonymousIdentity)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set anonymousIdentity: "+ + config.anonymousIdentity); + } + break setVariables; + } + + if ((config.password != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.passwordVarName, + config.password)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set password: "+ + config.password); + } + break setVariables; + } + + if ((config.clientCert != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.clientCertVarName, + config.clientCert)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set clientCert: "+ + config.clientCert); + } + break setVariables; + } + + if ((config.caCert != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.caCertVarName, + config.caCert)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set caCert: "+ + config.caCert); + } + break setVariables; + } + + if ((config.privateKey != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.privateKeyVarName, + config.privateKey)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set privateKey: "+ + config.privateKey); + } + break setVariables; + } + + if ((config.privateKeyPasswd != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.privateKeyPasswdVarName, + config.privateKeyPasswd)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set privateKeyPasswd: "+ + config.privateKeyPasswd); + } + break setVariables; + } + return netId; } @@ -1353,6 +1450,16 @@ public class WifiService extends IWifiManager.Stub { } } } + // Inform the backup manager about a data change + IBackupManager ibm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + if (ibm != null) { + try { + ibm.dataChanged("com.android.providers.settings"); + } catch (Exception e) { + // Try again later + } + } return result; } @@ -1449,10 +1556,12 @@ public class WifiService extends IWifiManager.Stub { Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); if (action.equals(Intent.ACTION_SCREEN_ON)) { + Log.d(TAG, "ACTION_SCREEN_ON"); mAlarmManager.cancel(mIdleIntent); mDeviceIdle = false; mScreenOff = false; } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { + Log.d(TAG, "ACTION_SCREEN_OFF"); mScreenOff = true; /* * Set a timer to put Wi-Fi to sleep, but only if the screen is off @@ -1461,12 +1570,21 @@ public class WifiService extends IWifiManager.Stub { * or plugged in to AC). */ if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) { - long triggerTime = System.currentTimeMillis() + idleMillis; - mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); + WifiInfo info = mWifiStateTracker.requestConnectionInfo(); + if (info.getSupplicantState() != SupplicantState.COMPLETED) { + // do not keep Wifi awake when screen is off if Wifi is not associated + mDeviceIdle = true; + updateWifiState(); + } else { + long triggerTime = System.currentTimeMillis() + idleMillis; + Log.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"); mDeviceIdle = true; } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { /* @@ -1477,9 +1595,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); if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) && !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) { long triggerTime = System.currentTimeMillis() + idleMillis; + Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms"); mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); mPluggedType = pluggedType; return; @@ -1732,7 +1852,7 @@ public class WifiService extends IWifiManager.Stub { } } - private class WifiLock extends WifiDeathRecipient { + private class WifiLock extends DeathRecipient { WifiLock(int lockMode, String tag, IBinder binder) { super(lockMode, tag, binder); } @@ -1780,7 +1900,9 @@ public class WifiService extends IWifiManager.Stub { private WifiLock removeLock(IBinder binder) { int index = findLockByBinder(binder); if (index >= 0) { - return mList.remove(index); + WifiLock ret = mList.remove(index); + ret.unlinkDeathRecipient(); + return ret; } else { return null; } @@ -1875,13 +1997,13 @@ public class WifiService extends IWifiManager.Stub { return hadLock; } - private abstract class WifiDeathRecipient + private abstract class DeathRecipient implements IBinder.DeathRecipient { String mTag; int mMode; IBinder mBinder; - WifiDeathRecipient(int mode, String tag, IBinder binder) { + DeathRecipient(int mode, String tag, IBinder binder) { super(); mTag = tag; mMode = mode; @@ -1892,15 +2014,19 @@ public class WifiService extends IWifiManager.Stub { binderDied(); } } + + void unlinkDeathRecipient() { + mBinder.unlinkToDeath(this, 0); + } } - private class WifiMulticaster extends WifiDeathRecipient { - WifiMulticaster(String tag, IBinder binder) { + private class Multicaster extends DeathRecipient { + Multicaster(String tag, IBinder binder) { super(Binder.getCallingUid(), tag, binder); } public void binderDied() { - Log.e(TAG, "WifiMulticaster binderDied"); + Log.e(TAG, "Multicaster binderDied"); synchronized (mMulticasters) { int i = mMulticasters.indexOf(this); if (i != -1) { @@ -1910,7 +2036,7 @@ public class WifiService extends IWifiManager.Stub { } public String toString() { - return "WifiMulticaster{" + mTag + " binder=" + mBinder + "}"; + return "Multicaster{" + mTag + " binder=" + mBinder + "}"; } public int getUid() { @@ -1918,12 +2044,12 @@ public class WifiService extends IWifiManager.Stub { } } - public void enableWifiMulticast(IBinder binder, String tag) { - enforceChangePermission(); + public void acquireMulticastLock(IBinder binder, String tag) { + enforceMulticastChangePermission(); synchronized (mMulticasters) { mMulticastEnabled++; - mMulticasters.add(new WifiMulticaster(tag, binder)); + mMulticasters.add(new Multicaster(tag, binder)); // Note that we could call stopPacketFiltering only when // our new size == 1 (first call), but this function won't // be called often and by making the stopPacket call each @@ -1941,15 +2067,15 @@ public class WifiService extends IWifiManager.Stub { } } - public void disableWifiMulticast() { - enforceChangePermission(); + public void releaseMulticastLock() { + enforceMulticastChangePermission(); int uid = Binder.getCallingUid(); synchronized (mMulticasters) { mMulticastDisabled++; int size = mMulticasters.size(); for (int i = size - 1; i >= 0; i--) { - WifiMulticaster m = mMulticasters.get(i); + Multicaster m = mMulticasters.get(i); if ((m != null) && (m.getUid() == uid)) { removeMulticasterLocked(i, uid); } @@ -1959,7 +2085,10 @@ public class WifiService extends IWifiManager.Stub { private void removeMulticasterLocked(int i, int uid) { - mMulticasters.remove(i); + Multicaster removed = mMulticasters.remove(i); + if (removed != null) { + removed.unlinkDeathRecipient(); + } if (mMulticasters.size() == 0) { WifiNative.startPacketFiltering(); } @@ -1973,7 +2102,7 @@ public class WifiService extends IWifiManager.Stub { } } - public boolean isWifiMulticastEnabled() { + public boolean isMulticastEnabled() { enforceAccessPermission(); synchronized (mMulticasters) { diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 3fa5baf..2dd70ef 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -77,6 +77,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.TokenWatcher; import android.provider.Settings; +import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.SparseIntArray; @@ -133,16 +134,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo static final boolean DEBUG_STARTING_WINDOW = false; static final boolean DEBUG_REORDER = false; static final boolean SHOW_TRANSACTIONS = false; - + static final boolean PROFILE_ORIENTATION = false; static final boolean BLUR = true; static final boolean localLOGV = DEBUG; - + static final int LOG_WM_NO_SURFACE_MEMORY = 31000; - + /** How long to wait for first key repeat, in milliseconds */ static final int KEY_REPEAT_FIRST_DELAY = 750; - + /** How long to wait for subsequent key repeats, in milliseconds */ static final int KEY_REPEAT_DELAY = 50; @@ -150,16 +151,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * for multiple windows of the same type and Z-ordering adjustment * with TYPE_LAYER_OFFSET. */ static final int TYPE_LAYER_MULTIPLIER = 10000; - + /** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above * or below others in the same layer. */ static final int TYPE_LAYER_OFFSET = 1000; - + /** How much to increment the layer for each window, to reserve room * for effect surfaces between them. */ static final int WINDOW_LAYER_MULTIPLIER = 5; - + /** The maximum length we will accept for a loaded animation duration: * this is 10 seconds. */ @@ -173,21 +174,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo /** Adjustment to time to perform a dim, to make it more dramatic. */ static final int DIM_DURATION_MULTIPLIER = 6; + + static final int INJECT_FAILED = 0; + static final int INJECT_SUCCEEDED = 1; + static final int INJECT_NO_PERMISSION = -1; static final int UPDATE_FOCUS_NORMAL = 0; static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; static final int UPDATE_FOCUS_PLACING_SURFACES = 2; static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3; - + /** The minimum time between dispatching touch events. */ int mMinWaitTimeBetweenTouchEvents = 1000 / 35; // Last touch event time long mLastTouchEventTime = 0; - + // Last touch event type int mLastTouchEventType = OTHER_EVENT; - + // Time to wait before calling useractivity again. This saves CPU usage // when we get a flood of touch events. static final int MIN_TIME_BETWEEN_USERACTIVITIES = 1000; @@ -195,10 +200,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Last time we call user activity long mLastUserActivityCallTime = 0; - // Last time we updated battery stats + // Last time we updated battery stats long mLastBatteryStatsCallTime = 0; - + private static final String SYSTEM_SECURE = "ro.secure"; + private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; /** * Condition waited on by {@link #reenableKeyguard} to know the call to @@ -224,20 +230,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final Context mContext; final boolean mHaveInputMethods; - + final boolean mLimitedAlphaCompositing; - + final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager(); final IActivityManager mActivityManager; - + final IBatteryStats mBatteryStats; - + /** * All currently active sessions with clients. */ final HashSet<Session> mSessions = new HashSet<Session>(); - + /** * Mapping from an IWindow IBinder to the server's Window object. * This is also used as the lock for all of our state. @@ -255,7 +261,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * over them. */ final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>(); - + /** * Window tokens that are in the process of exiting, but still * on screen for animations. @@ -314,9 +320,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * list or contain windows that need to be force removed. */ ArrayList<WindowState> mForceRemoves; - + IInputMethodManager mInputMethodManager; - + SurfaceSession mFxSession; Surface mDimSurface; boolean mDimShown; @@ -326,9 +332,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo long mLastDimAnimTime; Surface mBlurSurface; boolean mBlurShown; - + int mTransactionSequence = 0; - + final float[] mTmpFloats = new float[9]; boolean mSafeMode; @@ -340,7 +346,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int mLastRotationFlags; ArrayList<IRotationWatcher> mRotationWatchers = new ArrayList<IRotationWatcher>(); - + boolean mLayoutNeeded = true; boolean mAnimationPending = false; boolean mDisplayFrozen = false; @@ -352,7 +358,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // perform a rotation animation when turning off shows the lock screen which // changes the orientation. PowerManager.WakeLock mScreenFrozenLock; - + // State management of app transitions. When we are preparing for a // transition, mNextAppTransition will be the kind of transition to // perform or TRANSIT_NONE if we are not waiting. If we are waiting, @@ -365,40 +371,40 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean mSkipAppTransitionAnimation = false; final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>(); final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>(); - + //flag to detect fat touch events boolean mFatTouch = false; Display mDisplay; - + H mH = new H(); WindowState mCurrentFocus = null; WindowState mLastFocus = null; - + // This just indicates the window the input method is on top of, not // necessarily the window its input is going to. WindowState mInputMethodTarget = null; WindowState mUpcomingInputMethodTarget = null; boolean mInputMethodTargetWaitingAnim; int mInputMethodAnimLayerAdjustment; - + WindowState mInputMethodWindow = null; final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>(); AppWindowToken mFocusedApp = null; PowerManagerService mPowerManager; - + float mWindowAnimationScale = 1.0f; float mTransitionAnimationScale = 1.0f; - + final KeyWaiter mKeyWaiter = new KeyWaiter(); final KeyQ mQueue; final InputDispatcherThread mInputThread; // Who is holding the screen on. Session mHoldingScreenOn; - + /** * Whether the UI is currently running in touch mode (not showing * navigational focus because the user is directly pressing the screen). @@ -408,14 +414,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private ViewServer mViewServer; final Rect mTempRect = new Rect(); - + final Configuration mTempConfiguration = new Configuration(); + int screenLayout = Configuration.SCREENLAYOUT_UNDEFINED; public static WindowManagerService main(Context context, PowerManagerService pm, boolean haveInputMethods) { WMThread thr = new WMThread(context, pm, haveInputMethods); thr.start(); - + synchronized (thr) { while (thr.mService == null) { try { @@ -424,17 +431,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + return thr.mService; } - + static class WMThread extends Thread { WindowManagerService mService; - + private final Context mContext; private final PowerManagerService mPM; private final boolean mHaveInputMethods; - + public WMThread(Context context, PowerManagerService pm, boolean haveInputMethods) { super("WindowManager"); @@ -442,19 +449,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mPM = pm; mHaveInputMethods = haveInputMethods; } - + public void run() { Looper.prepare(); WindowManagerService s = new WindowManagerService(mContext, mPM, mHaveInputMethods); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_DISPLAY); - + synchronized (this) { mService = s; notifyAll(); } - + Looper.loop(); } } @@ -465,7 +472,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private final Context mContext; private final PowerManagerService mPM; boolean mRunning = false; - + public PolicyThread(WindowManagerPolicy policy, WindowManagerService service, Context context, PowerManagerService pm) { @@ -475,7 +482,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mContext = context; mPM = pm; } - + public void run() { Looper.prepare(); //Looper.myLooper().setMessageLogging(new LogPrinter( @@ -483,12 +490,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); mPolicy.init(mContext, mService, mPM); - + synchronized (this) { mRunning = true; notifyAll(); } - + Looper.loop(); } } @@ -499,7 +506,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mHaveInputMethods = haveInputMethods; mLimitedAlphaCompositing = context.getResources().getBoolean( com.android.internal.R.bool.config_sf_limitedAlpha); - + mPowerManager = pm; mPowerManager.setPolicy(mPolicy); PowerManager pmc = (PowerManager)context.getSystemService(Context.POWER_SERVICE); @@ -515,14 +522,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale); mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(), Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale); - + mQueue = new KeyQ(); mInputThread = new InputDispatcherThread(); - + PolicyThread thr = new PolicyThread(mPolicy, this, context, pm); thr.start(); - + synchronized (thr) { while (!thr.mRunning) { try { @@ -531,9 +538,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + mInputThread.start(); - + // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); } @@ -586,12 +593,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return -1; } - + private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) { final IWindow client = win.mClient; final WindowToken token = win.mToken; final ArrayList localmWindows = mWindows; - + final int N = localmWindows.size(); final WindowState attached = win.mAttachedWindow; int i; @@ -616,12 +623,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { int newIdx = findIdxBasedOnAppTokens(win); if(newIdx != -1) { - //there is a window above this one associated with the same - //apptoken note that the window could be a floating window - //that was created later or a window at the top of the list of + //there is a window above this one associated with the same + //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. localmWindows.add(newIdx+1, win); - } + } } } } else { @@ -647,7 +654,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // we need to look some more. if (pos != null) { // Move behind any windows attached to this one. - WindowToken atoken = + WindowToken atoken = mTokenMap.get(((WindowState)pos).mClient.asBinder()); if (atoken != null) { final int NC = atoken.windows.size(); @@ -770,12 +777,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + if (win.mAppToken != null && addToToken) { win.mAppToken.allAppWindows.add(win); } } - + static boolean canBeImeTarget(WindowState w) { final int fl = w.mAttrs.flags & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM); @@ -784,7 +791,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return false; } - + int findDesiredInputMethodWindowIndexLocked(boolean willMove) { final ArrayList localmWindows = mWindows; final int N = localmWindows.size(); @@ -793,12 +800,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo while (i > 0) { i--; w = (WindowState)localmWindows.get(i); - + //Log.i(TAG, "Checking window @" + i + " " + w + " fl=0x" // + Integer.toHexString(w.mAttrs.flags)); if (canBeImeTarget(w)) { //Log.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 // it is not -really- where input will go. So if the caller @@ -816,16 +823,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo break; } } - + mUpcomingInputMethodTarget = w; - + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Desired input method target=" + w + " willMove=" + willMove); - + if (willMove && w != null) { final WindowState curTarget = mInputMethodTarget; if (curTarget != null && curTarget.mAppToken != null) { - + // Now some fun for dealing with window animations that // modify the Z order. We need to look at all windows below // the current target that are in this app, finding the highest @@ -851,14 +858,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pos--; } } - + if (highestTarget != null) { - if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" + if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" + mNextAppTransition + " " + highestTarget + " animating=" + highestTarget.isAnimating() + " layer=" + highestTarget.mAnimLayer + " new layer=" + w.mAnimLayer); - + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { // If we are currently setting up for an animation, // hold everything until we can find out what will happen. @@ -877,7 +884,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + //Log.i(TAG, "Placing input method @" + (i+1)); if (w != null) { if (willMove) { @@ -904,7 +911,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return -1; } - + void addInputMethodWindowToListLocked(WindowState win) { int pos = findDesiredInputMethodWindowIndexLocked(true); if (pos >= 0) { @@ -917,7 +924,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo addWindowToListInOrderLocked(win, true); moveInputMethodDialogsLocked(pos); } - + void setInputMethodAnimLayerAdjustment(int adj) { if (DEBUG_LAYERS) Log.v(TAG, "Setting im layer adj to " + adj); mInputMethodAnimLayerAdjustment = adj; @@ -944,7 +951,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " anim layer: " + imw.mAnimLayer); } } - + private int tmpRemoveWindowLocked(int interestingPos, WindowState win) { int wpos = mWindows.indexOf(win); if (wpos >= 0) { @@ -963,7 +970,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return interestingPos; } - + private void reAddWindowToListInOrderLocked(WindowState win) { addWindowToListInOrderLocked(win, false); // This is a hack to get all of the child windows added as well @@ -975,7 +982,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo reAddWindowLocked(wpos, win); } } - + void logWindowList(String prefix) { int N = mWindows.size(); while (N > 0) { @@ -983,10 +990,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.v(TAG, prefix + "#" + N + ": " + mWindows.get(N)); } } - + void moveInputMethodDialogsLocked(int pos) { ArrayList<WindowState> dialogs = mInputMethodDialogs; - + final int N = dialogs.size(); if (DEBUG_INPUT_METHOD) Log.v(TAG, "Removing " + N + " dialogs w/pos=" + pos); for (int i=0; i<N; i++) { @@ -996,7 +1003,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.v(TAG, "Window list w/pos=" + pos); logWindowList(" "); } - + if (pos >= 0) { final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; if (pos < mWindows.size()) { @@ -1027,25 +1034,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + boolean moveInputMethodWindowsIfNeededLocked(boolean needAssignLayers) { final WindowState imWin = mInputMethodWindow; final int DN = mInputMethodDialogs.size(); if (imWin == null && DN == 0) { return false; } - + int imPos = findDesiredInputMethodWindowIndexLocked(true); if (imPos >= 0) { // In this case, the input method windows are to be placed // immediately above the window they are targeting. - + // First check to see if the input method windows are already // located here, and contiguous. final int N = mWindows.size(); WindowState firstImWin = imPos < N ? (WindowState)mWindows.get(imPos) : null; - + // Figure out the actual input method window that should be // at the bottom of their stack. WindowState baseImWin = imWin != null @@ -1054,7 +1061,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo WindowState cw = (WindowState)baseImWin.mChildWindows.get(0); if (cw.mSubLayer < 0) baseImWin = cw; } - + if (firstImWin == baseImWin) { // The windows haven't moved... but are they still contiguous? // First find the top IM window. @@ -1078,7 +1085,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return false; } } - + if (imWin != null) { if (DEBUG_INPUT_METHOD) { Log.v(TAG, "Moving IM from " + imPos); @@ -1099,11 +1106,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { moveInputMethodDialogsLocked(imPos); } - + } else { // In this case, the input method windows go in a fixed layer, // because they aren't currently associated with a focus window. - + if (imWin != null) { if (DEBUG_INPUT_METHOD) Log.v(TAG, "Moving IM from " + imPos); tmpRemoveWindowLocked(0, imWin); @@ -1117,20 +1124,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { moveInputMethodDialogsLocked(-1);; } - + } - + if (needAssignLayers) { assignLayersLocked(); } - + return true; } - + void adjustInputMethodDialogsLocked() { moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); } - + public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets) { @@ -1138,11 +1145,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (res != WindowManagerImpl.ADD_OKAY) { return res; } - + boolean reportNewConfig = false; WindowState attachedWindow = null; WindowState win = null; - + synchronized(mWindowMap) { // Instantiating a Display requires talking with the simulator, // so don't do it until we know the system is mostly up and @@ -1153,14 +1160,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.setDisplay(mDisplay); reportNewConfig = true; } - + if (mWindowMap.containsKey(client.asBinder())) { Log.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); if (attachedWindow == null) { Log.w(TAG, "Attempted to add window with token that is not a window: " + attrs.token + ". Aborting."); @@ -1227,7 +1234,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } mPolicy.adjustWindowParamsLw(win.mAttrs); - + res = mPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerImpl.ADD_OKAY) { return res; @@ -1236,9 +1243,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // From now on, no exceptions or errors allowed! res = WindowManagerImpl.ADD_OKAY; - + final long origId = Binder.clearCallingIdentity(); - + if (addToken) { mTokenMap.put(attrs.token, token); mTokenList.add(token); @@ -1252,7 +1259,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } boolean imMayMove = true; - + if (attrs.type == TYPE_INPUT_METHOD) { mInputMethodWindow = win; addInputMethodWindowToListLocked(win); @@ -1265,18 +1272,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { addWindowToListInOrderLocked(win, true); } - + win.mEnterAnimationPending = true; - + mPolicy.getContentInsetHintLw(attrs, outContentInsets); - + if (mInTouchMode) { res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE; } if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) { res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE; } - + boolean focusChanged = false; if (win.canReceiveKeys()) { if ((focusChanged=updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS)) @@ -1284,15 +1291,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo imMayMove = false; } } - + if (imMayMove) { - moveInputMethodWindowsIfNeededLocked(false); + moveInputMethodWindowsIfNeededLocked(false); } - + assignLayersLocked(); // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. - + //dump(); if (focusChanged) { @@ -1300,7 +1307,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mKeyWaiter.handleNewWindowLocked(mCurrentFocus); } } - if (localLOGV) Log.v( TAG, "New client " + client.asBinder() + ": window=" + win); @@ -1317,16 +1323,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Update Orientation after adding a window, only if the window needs to be // displayed right away if (win.isVisibleOrAdding()) { - if (updateOrientationFromAppTokens(null, null) != null) { + if (updateOrientationFromAppTokensUnchecked(null, null) != null) { sendNewConfiguration(); } } } Binder.restoreCallingIdentity(origId); - + return res; } - + public void removeWindow(Session session, IWindow client) { synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client); @@ -1336,7 +1342,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo removeWindowLocked(session, win); } } - + public void removeWindowLocked(Session session, WindowState win) { if (localLOGV || DEBUG_FOCUS) Log.v( @@ -1346,7 +1352,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + ", surface=" + win.mSurface); final long origId = Binder.clearCallingIdentity(); - + if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Remove " + win + ": mSurface=" + win.mSurface + " mExiting=" + win.mExiting @@ -1366,7 +1372,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // If we are not currently running the exit animation, we // need to see about starting one. if (wasVisible=win.isWinVisibleLw()) { - + int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; @@ -1403,17 +1409,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); Binder.restoreCallingIdentity(origId); } - + private void removeWindowInnerLocked(Session session, WindowState win) { mKeyWaiter.releasePendingPointerLocked(win.mSession); mKeyWaiter.releasePendingTrackballLocked(win.mSession); - + win.mRemoved = true; - + if (mInputMethodTarget == win) { moveInputMethodWindowsIfNeededLocked(false); } - + mPolicy.removeWindowLw(win); win.removeLocked(); @@ -1425,7 +1431,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.remove(win); } - + final WindowToken token = win.mToken; final AppWindowToken atoken = win.mAppToken; token.windows.remove(win); @@ -1462,7 +1468,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mH.sendMessage(m); } } - + if (!mInLayout) { assignLayersLocked(); mLayoutNeeded = true; @@ -1493,7 +1499,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } void setInsetsWindow(Session session, IWindow client, - int touchableInsets, Rect contentInsets, + int touchableInsets, Rect contentInsets, Rect visibleInsets) { long origId = Binder.clearCallingIdentity(); try { @@ -1512,7 +1518,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Binder.restoreCallingIdentity(origId); } } - + public void getWindowDisplayFrame(Session session, IWindow client, Rect outDisplayFrame) { synchronized(mWindowMap) { @@ -1534,7 +1540,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean inTouchMode; Configuration newConfig = null; long origId = Binder.clearCallingIdentity(); - + synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client); if (win == null) { @@ -1546,7 +1552,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (attrs != null) { mPolicy.adjustWindowParamsLw(attrs); } - + int attrChanges = 0; int flagChanges = 0; if (attrs != null) { @@ -1578,11 +1584,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean imMayMove = (flagChanges&( WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0; - + boolean focusMayChange = win.mViewVisibility != viewVisibility || ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) || (!win.mRelayoutCalled); - + win.mRelayoutCalled = true; final int oldVisibility = win.mViewVisibility; win.mViewVisibility = viewVisibility; @@ -1670,17 +1676,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus); } - + // updateFocusedWindowLocked() already assigned layers so we only need to // reassign them at this point if the IM window state gets shuffled boolean assignLayers = false; - + if (imMayMove) { if (moveInputMethodWindowsIfNeededLocked(false)) { assignLayers = true; } } - + mLayoutNeeded = true; win.mGivenInsetsPending = insetsPending; if (assignLayers) { @@ -1696,7 +1702,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo outVisibleInsets.set(win.mVisibleInsets); if (localLOGV) Log.v( TAG, "Relayout given client " + client.asBinder() - + ", requestedWidth=" + requestedWidth + + ", requestedWidth=" + requestedWidth + ", requestedHeight=" + requestedHeight + ", viewVisibility=" + viewVisibility + "\nRelayout returning frame=" + outFrame @@ -1711,9 +1717,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (newConfig != null) { sendNewConfiguration(); } - + Binder.restoreCallingIdentity(origId); - + return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0) | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0); } @@ -1750,7 +1756,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return null; } - + private void applyEnterAnimationLocked(WindowState win) { int transit = WindowManagerPolicy.TRANSIT_SHOW; if (win.mEnterAnimationPending) { @@ -1768,7 +1774,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // an animation of the same type, then just leave that one alone. return true; } - + // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation @@ -1833,7 +1839,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return null; } - + private boolean applyAnimationLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, int transit, boolean enter) { // Only apply an animation if the display isn't frozen. If it is @@ -1932,7 +1938,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (Binder.getCallingPid() == Process.myPid()) { return true; } - + if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) { return true; @@ -1944,7 +1950,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.w(TAG, msg); return false; } - + AppWindowToken findAppWindowToken(IBinder token) { WindowToken wtoken = mTokenMap.get(token); if (wtoken == null) { @@ -1952,13 +1958,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return wtoken.appWindowToken; } - + public void addWindowToken(IBinder token, int type) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addWindowToken()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - + synchronized(mWindowMap) { WindowToken wtoken = mTokenMap.get(token); if (wtoken != null) { @@ -1970,11 +1976,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mTokenList.add(wtoken); } } - + public void removeWindowToken(IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "removeWindowToken()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } final long origId = Binder.clearCallingIdentity(); @@ -1985,17 +1991,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean delayed = false; if (!wtoken.hidden) { wtoken.hidden = true; - + final int N = wtoken.windows.size(); boolean changed = false; - + for (int i=0; i<N; i++) { WindowState win = wtoken.windows.get(i); if (win.isAnimating()) { delayed = true; } - + if (win.isVisibleNow()) { applyAnimationLocked(win, WindowManagerPolicy.TRANSIT_EXIT, false); @@ -2010,12 +2016,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo performLayoutAndPlaceSurfacesLocked(); updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); } - + if (delayed) { mExitingTokens.add(wtoken); } } - + } else { Log.w(TAG, "Attempted to remove non-existing token: " + token); } @@ -2027,9 +2033,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int groupId, int requestedOrientation, boolean fullscreen) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addAppToken()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - + synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken != null) { @@ -2044,19 +2050,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (localLOGV) Log.v(TAG, "Adding new app token: " + wtoken); mTokenMap.put(token.asBinder(), wtoken); mTokenList.add(wtoken); - + // Application tokens start out hidden. wtoken.hidden = true; wtoken.hiddenRequested = true; - + //dump(); } } - + public void setAppGroupId(IBinder token, int groupId) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppStartingIcon()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { @@ -2068,7 +2074,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wtoken.groupId = groupId; } } - + public int getOrientationFromWindowsLocked() { int pos = mWindows.size() - 1; while (pos >= 0) { @@ -2092,7 +2098,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - + public int getOrientationFromAppTokensLocked() { int pos = mAppTokens.size() - 1; int curGroup = 0; @@ -2134,7 +2140,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // to use the orientation behind it, then just take whatever // orientation it has and ignores whatever is under it. lastFullscreen = wtoken.appFullscreen; - if (lastFullscreen + if (lastFullscreen && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { return or; } @@ -2151,11 +2157,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - + public Configuration updateOrientationFromAppTokens( Configuration currentConfig, IBinder freezeThisOneIfNeeded) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "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; synchronized(mWindowMap) { config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded); } @@ -2163,14 +2183,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } - Binder.restoreCallingIdentity(ident); return config; } - + /* * The orientation is computed from non-application windows first. If none of * the non-application windows specify orientation, the orientation is computed from - * application tokens. + * application tokens. * @see android.view.IWindowManager#updateOrientationFromAppTokens( * android.os.IBinder) */ @@ -2180,7 +2199,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo long ident = Binder.clearCallingIdentity(); try { int req = computeForcedAppOrientationLocked(); - + if (req != mForcedAppOrientation) { changed = true; mForcedAppOrientation = req; @@ -2188,7 +2207,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo //action like disabling/enabling sensors etc., mPolicy.setCurrentOrientationLw(req); } - + if (changed) { changed = setRotationUncheckedLocked( WindowManagerPolicy.USE_LAST_ROTATION, @@ -2220,10 +2239,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } finally { Binder.restoreCallingIdentity(ident); } - + return null; } - + int computeForcedAppOrientationLocked() { int req = getOrientationFromWindowsLocked(); if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { @@ -2231,39 +2250,39 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return req; } - + public void setAppOrientation(IApplicationToken token, int requestedOrientation) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppOrientation()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - + synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken == null) { Log.w(TAG, "Attempted to set orientation of non-existing app token: " + token); return; } - + wtoken.requestedOrientation = requestedOrientation; } } - + public int getAppOrientation(IApplicationToken token) { synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken == null) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - + return wtoken.requestedOrientation; } } - + public void setFocusedApp(IBinder token, boolean moveFocusNow) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setFocusedApp()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { @@ -2296,9 +2315,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void prepareAppTransition(int transit) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "prepareAppTransition()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - + synchronized(mWindowMap) { if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Prepare app transition: transit=" + transit @@ -2306,6 +2325,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!mDisplayFrozen) { if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { mNextAppTransition = transit; + } else if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN + && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) { + // Opening a new task always supersedes a close for the anim. + mNextAppTransition = transit; + } else if (transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN + && mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) { + // Opening a new activity always supersedes a close for the anim. + mNextAppTransition = transit; } mAppTransitionReady = false; mAppTransitionTimeout = false; @@ -2321,13 +2348,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public int getPendingAppTransition() { return mNextAppTransition; } - + public void executeAppTransition() { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "executeAppTransition()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - + synchronized(mWindowMap) { if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Execute app transition: mNextAppTransition=" + mNextAppTransition); @@ -2345,14 +2372,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo IBinder transferFrom, boolean createIfNeeded) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppStartingIcon()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { if (DEBUG_STARTING_WINDOW) Log.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); @@ -2365,11 +2392,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mDisplayFrozen) { return; } - + if (wtoken.startingData != null) { return; } - + if (transferFrom != null) { AppWindowToken ttoken = findAppWindowToken(transferFrom); if (ttoken != null) { @@ -2385,7 +2412,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo "Moving existing starting from " + ttoken + " to " + wtoken); final long origId = Binder.clearCallingIdentity(); - + // Transfer the starting window over to the new // token. wtoken.startingData = ttoken.startingData; @@ -2403,7 +2430,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo ttoken.allAppWindows.remove(startingWindow); addWindowToListInOrderLocked(startingWindow, true); wtoken.allAppWindows.add(startingWindow); - + // Propagate other interesting state between the // tokens. If the old token is displayed, we should // immediately force the new one to be displayed. If @@ -2433,7 +2460,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wtoken.updateLayers(); ttoken.updateLayers(); } - + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); @@ -2463,7 +2490,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!createIfNeeded) { return; } - + mStartingIconInTransition = true; wtoken.startingData = new StartingData( pkg, theme, nonLocalizedLabel, @@ -2479,7 +2506,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void setAppWillBeHidden(IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppWillBeHidden()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } AppWindowToken wtoken; @@ -2493,7 +2520,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wtoken.willBeHidden = true; } } - + boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, boolean visible, int transit, boolean performLayout) { boolean delayed = false; @@ -2502,7 +2529,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wtoken.clientHidden = !visible; wtoken.sendAppVisibilityToClients(); } - + wtoken.willBeHidden = false; if (wtoken.hidden == visible) { final int N = wtoken.allAppWindows.size(); @@ -2510,9 +2537,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden + " performLayout=" + performLayout); - + boolean runningAppAnimation = false; - + if (transit != WindowManagerPolicy.TRANSIT_NONE) { if (wtoken.animation == sDummyAnimation) { wtoken.animation = null; @@ -2523,7 +2550,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo delayed = runningAppAnimation = true; } } - + for (int i=0; i<N; i++) { WindowState win = wtoken.allAppWindows.get(i); if (win == wtoken.startingWindow) { @@ -2533,7 +2560,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.isAnimating()) { delayed = true; } - + //Log.i(TAG, "Window " + win + ": vis=" + win.isVisible()); //win.dump(" "); if (visible) { @@ -2568,11 +2595,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo swin.mPolicyVisibilityAfterAnim = false; } } - + if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "setTokenVisibilityLocked: " + wtoken + ": hidden=" + wtoken.hidden + " hiddenRequested=" + wtoken.hiddenRequested); - + if (changed && performLayout) { mLayoutNeeded = true; updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); @@ -2583,14 +2610,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (wtoken.animation != null) { delayed = true; } - + return delayed; } public void setAppVisibility(IBinder token, boolean visible) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppVisibility()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } AppWindowToken wtoken; @@ -2610,7 +2637,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " hidden=" + wtoken.hidden + " hiddenRequested=" + wtoken.hiddenRequested, e); } - + // If we are preparing an app transition, then delay changing // the visibility of this token until we execute that transition. if (!mDisplayFrozen && mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { @@ -2619,7 +2646,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return; } wtoken.hiddenRequested = !visible; - + if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Setting dummy animation on: " + wtoken); wtoken.setDummyAnimation(); @@ -2631,7 +2658,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wtoken.allDrawn = false; wtoken.startingDisplayed = false; wtoken.startingMoved = false; - + if (wtoken.clientHidden) { // In the case where we are making an app visible // but holding off for a transition, we still need @@ -2647,7 +2674,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return; } - + final long origId = Binder.clearCallingIdentity(); setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_NONE, true); wtoken.updateReportedVisibilityLocked(); @@ -2688,7 +2715,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + public void startAppFreezingScreenLocked(AppWindowToken wtoken, int configChanges) { if (DEBUG_ORIENTATION) { @@ -2716,11 +2743,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + public void startAppFreezingScreen(IBinder token, int configChanges) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppFreezingScreen()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { @@ -2728,7 +2755,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (DEBUG_ORIENTATION) Log.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); @@ -2739,11 +2766,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Binder.restoreCallingIdentity(origId); } } - + public void stopAppFreezingScreen(IBinder token, boolean force) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppFreezingScreen()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { @@ -2758,11 +2785,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Binder.restoreCallingIdentity(origId); } } - + public void removeAppToken(IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "removeAppToken()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } AppWindowToken wtoken = null; @@ -2807,7 +2834,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { Log.w(TAG, "Attempted to remove non-existing app token: " + token); } - + if (!delayed && wtoken != null) { wtoken.updateReportedVisibilityLocked(); } @@ -2841,13 +2868,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.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)); } } - + private int findWindowOffsetLocked(int tokenPos) { final int NW = mWindows.size(); @@ -2918,7 +2945,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return index; } - + private final int reAddAppWindowsLocked(int index, WindowToken token) { final int NW = token.windows.size(); for (int i=0; i<NW; i++) { @@ -2930,7 +2957,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void moveAppToken(int index, IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "moveAppToken()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { @@ -2945,7 +2972,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mAppTokens.add(index, wtoken); if (DEBUG_REORDER) Log.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) dumpWindowsLocked(); @@ -3012,7 +3039,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void moveAppTokensToTop(List<IBinder> tokens) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "moveAppTokensToTop()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } final long origId = Binder.clearCallingIdentity(); @@ -3033,7 +3060,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void moveAppTokensToBottom(List<IBinder> tokens) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "moveAppTokensToBottom()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } final long origId = Binder.clearCallingIdentity(); @@ -3056,7 +3083,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // ------------------------------------------------------------- // Misc IWindowSession methods // ------------------------------------------------------------- - + public void disableKeyguard(IBinder token, String tag) { if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD) != PackageManager.PERMISSION_GRANTED) { @@ -3110,17 +3137,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public boolean inKeyguardRestrictedInputMode() { return mPolicy.inKeyguardRestrictedKeyInputMode(); } - + static float fixScale(float scale) { if (scale < 0) scale = 0; else if (scale > 20) scale = 20; return Math.abs(scale); } - + public void setAnimationScale(int which, float scale) { if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, "setAnimationScale()")) { - return; + throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); } if (scale < 0) scale = 0; @@ -3130,15 +3157,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo case 0: mWindowAnimationScale = fixScale(scale); break; case 1: mTransitionAnimationScale = fixScale(scale); break; } - + // Persist setting mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget(); } - + public void setAnimationScales(float[] scales) { if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, "setAnimationScale()")) { - return; + throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); } if (scales != null) { @@ -3149,11 +3176,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mTransitionAnimationScale = fixScale(scales[1]); } } - + // Persist setting mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget(); } - + public float getAnimationScale(int which) { switch (which) { case 0: return mWindowAnimationScale; @@ -3161,63 +3188,63 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return 0; } - + public float[] getAnimationScales() { return new float[] { mWindowAnimationScale, mTransitionAnimationScale }; } - + public int getSwitchState(int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getSwitchState()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getSwitchState(sw); } - + public int getSwitchStateForDevice(int devid, int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getSwitchStateForDevice()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getSwitchState(devid, sw); } - + public int getScancodeState(int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getScancodeState()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getScancodeState(sw); } - + public int getScancodeStateForDevice(int devid, int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getScancodeStateForDevice()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getScancodeState(devid, sw); } - + public int getKeycodeState(int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getKeycodeState()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getKeycodeState(sw); } - + public int getKeycodeStateForDevice(int devid, int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getKeycodeStateForDevice()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getKeycodeState(devid, sw); } - + public boolean hasKeys(int[] keycodes, boolean[] keyExists) { return KeyInputQueue.hasKeys(keycodes, keyExists); } - + public void enableScreenAfterBoot() { synchronized(mWindowMap) { if (mSystemBooted) { @@ -3225,10 +3252,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } mSystemBooted = true; } - + performEnableScreen(); } - + public void enableScreenIfNeededLocked() { if (mDisplayEnabled) { return; @@ -3238,7 +3265,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } mH.sendMessage(mH.obtainMessage(H.ENABLE_SCREEN)); } - + public void performEnableScreen() { synchronized(mWindowMap) { if (mDisplayEnabled) { @@ -3247,7 +3274,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!mSystemBooted) { return; } - + // Don't enable the screen until all existing windows // have been drawn. final int N = mWindows.size(); @@ -3257,7 +3284,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return; } } - + mDisplayEnabled = true; if (false) { Log.i(TAG, "ENABLING SCREEN!"); @@ -3280,41 +3307,41 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.e(TAG, "Boot completed: SurfaceFlinger is dead!"); } } - + mPolicy.enableScreenAfterBoot(); - + // Make sure the last requested orientation has been applied. setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false, mLastRotationFlags | Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE); } - + public void setInTouchMode(boolean mode) { synchronized(mWindowMap) { mInTouchMode = mode; } } - public void setRotation(int rotation, + public void setRotation(int rotation, boolean alwaysSendConfiguration, int animFlags) { if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, "setRotation()")) { - return; + throw new SecurityException("Requires SET_ORIENTATION permission"); } setRotationUnchecked(rotation, alwaysSendConfiguration, animFlags); } - + public void setRotationUnchecked(int rotation, boolean alwaysSendConfiguration, int animFlags) { if(DEBUG_ORIENTATION) Log.v(TAG, "alwaysSendConfiguration set to "+alwaysSendConfiguration); - + long origId = Binder.clearCallingIdentity(); boolean changed; synchronized(mWindowMap) { changed = setRotationUncheckedLocked(rotation, animFlags); } - + if (changed) { sendNewConfiguration(); synchronized(mWindowMap) { @@ -3325,10 +3352,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo //update configuration ignoring orientation change sendNewConfiguration(); } - + Binder.restoreCallingIdentity(origId); } - + public boolean setRotationUncheckedLocked(int rotation, int animFlags) { boolean changed; if (rotation == WindowManagerPolicy.USE_LAST_ROTATION) { @@ -3342,9 +3369,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mRotation, mDisplayEnabled); if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to " + rotation); changed = mDisplayEnabled && mRotation != rotation; - + if (changed) { - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Log.v(TAG, "Rotation changed to " + rotation + " from " + mRotation + " (forceApp=" + mForcedAppOrientation @@ -3373,10 +3400,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } //end if changed - + return changed; } - + public int getRotation() { return mRotation; } @@ -3388,14 +3415,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo synchronized (mWindowMap) { for (int i=0; i<mRotationWatchers.size(); i++) { if (watcherBinder == mRotationWatchers.get(i).asBinder()) { - mRotationWatchers.remove(i); + IRotationWatcher removed = mRotationWatchers.remove(i); + if (removed != null) { + removed.asBinder().unlinkToDeath(this, 0); + } i--; } } } } }; - + synchronized (mWindowMap) { try { watcher.asBinder().linkToDeath(dr, 0); @@ -3403,7 +3433,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RemoteException e) { // Client died, no cleanup needed. } - + return mRotation; } } @@ -3419,7 +3449,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * @see com.android.server.ViewServer#VIEW_SERVER_DEFAULT_PORT */ public boolean startViewServer(int port) { - if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) { + if (isSystemSecure()) { return false; } @@ -3436,7 +3466,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo try { return mViewServer.start(); } catch (IOException e) { - Log.w(TAG, "View server did not start"); + Log.w(TAG, "View server did not start"); } } return false; @@ -3451,6 +3481,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return false; } + private boolean isSystemSecure() { + return "1".equals(SystemProperties.get(SYSTEM_SECURE, "1")) && + "0".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); + } + /** * Stops the view server if it exists. * @@ -3460,7 +3495,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * @see com.android.server.ViewServer */ public boolean stopViewServer() { - if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) { + if (isSystemSecure()) { return false; } @@ -3482,7 +3517,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * @see com.android.server.ViewServer */ public boolean isViewServerRunning() { - if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) { + if (isSystemSecure()) { return false; } @@ -3503,7 +3538,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * @return False if an error occured, true otherwise. */ boolean viewServerListWindows(Socket client) { - if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) { + if (isSystemSecure()) { return false; } @@ -3570,7 +3605,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * not indicate whether the command itself was successful. */ boolean viewServerWindowCommand(Socket client, String command, String parameters) { - if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) { + if (isSystemSecure()) { return false; } @@ -3660,13 +3695,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RemoteException e) { } } - + public Configuration computeNewConfiguration() { synchronized (mWindowMap) { return computeNewConfigurationLocked(); } } - + Configuration computeNewConfigurationLocked() { Configuration config = new Configuration(); if (!computeNewConfigurationLocked(config)) { @@ -3687,7 +3722,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return config; } - + boolean computeNewConfigurationLocked(Configuration config) { if (mDisplay == null) { return false; @@ -3702,12 +3737,46 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo orientation = Configuration.ORIENTATION_LANDSCAPE; } config.orientation = orientation; + + if (screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) { + // Note we only do this once because at this point we don't + // expect the screen to change in this way at runtime, and want + // to avoid all of this computation for every config change. + DisplayMetrics dm = new DisplayMetrics(); + mDisplay.getMetrics(dm); + int longSize = dw; + int shortSize = dh; + if (longSize < shortSize) { + int tmp = longSize; + longSize = shortSize; + shortSize = tmp; + } + longSize = (int)(longSize/dm.density); + shortSize = (int)(shortSize/dm.density); + + // These semi-magic numbers define our compatibility modes for + // applications with different screens. Don't change unless you + // make sure to test lots and lots of apps! + if (longSize < 470) { + // This is shorter than an HVGA normal density screen (which + // is 480 pixels on its long side). + screenLayout = Configuration.SCREENLAYOUT_SMALL; + } else if (longSize > 490 && shortSize > 330) { + // This is larger than an HVGA normal density screen (which + // is 480x320 pixels). + screenLayout = Configuration.SCREENLAYOUT_LARGE; + } else { + screenLayout = Configuration.SCREENLAYOUT_NORMAL; + } + } + config.screenLayout = screenLayout; + config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO; config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; mPolicy.adjustConfigurationLw(config); return true; } - + // ------------------------------------------------------------- // Input Events and Focus Management // ------------------------------------------------------------- @@ -3771,15 +3840,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo /** * @return Returns true if event was dispatched, false if it was dropped for any reason */ - private boolean dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) { + private int dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) { if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Log.v(TAG, "dispatchPointer " + ev); Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev, - ev, true, false); - + ev, true, false, pid, uid); + int action = ev.getAction(); - + if (action == MotionEvent.ACTION_UP) { // let go of our target mKeyWaiter.mMotionTarget = null; @@ -3801,20 +3870,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.recycleEvent(qev); } ev.recycle(); - return false; + return INJECT_FAILED; } if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); - return true; + return INJECT_SUCCEEDED; } - + WindowState target = (WindowState)targetObj; - + final long eventTime = ev.getEventTime(); - + //Log.i(TAG, "Sending " + ev + " to " + target); if (uid != 0 && uid != target.mSession.mUid) { @@ -3828,11 +3897,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.recycleEvent(qev); } ev.recycle(); - return false; + return INJECT_NO_PERMISSION; } } - - if ((target.mAttrs.flags & + + if ((target.mAttrs.flags & WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) { //target wants to ignore fat touch events boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(ev); @@ -3859,7 +3928,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if(mFatTouch) { //two cases here //an invalid down followed by 0 or moves(valid or invalid) - //a valid down, invalid move, more moves. want to ignore till up + //a valid down, invalid move, more moves. want to ignore till up returnFlag = true; } else if(cheekPress) { //valid down followed by invalid moves @@ -3878,7 +3947,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.recycleEvent(qev); } ev.recycle(); - return false; + return INJECT_FAILED; } } //end if target @@ -3944,7 +4013,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mKeyWaiter.bindTargetWindowLocked(target); } } - + // finally offset the event to the target's coordinate system and // dispatch the event. try { @@ -3952,7 +4021,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.v(TAG, "Delivering pointer " + qev + " to " + target); } target.mClient.dispatchPointer(ev, eventTime); - return true; + return INJECT_SUCCEEDED; } catch (android.os.RemoteException e) { Log.i(TAG, "WINDOW DIED during motion dispatch: " + target); mKeyWaiter.mMotionTarget = null; @@ -3963,36 +4032,36 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // removed. } } - return false; + return INJECT_FAILED; } - + /** * @return Returns true if event was dispatched, false if it was dropped for any reason */ - private boolean dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) { + private int dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) { if (DEBUG_INPUT) Log.v( TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">"); - + Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev, - ev, false, false); + ev, false, false, pid, uid); if (focusObj == null) { Log.w(TAG, "No focus window, dropping trackball: " + ev); if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); - return false; + return INJECT_FAILED; } if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); - return true; + return INJECT_SUCCEEDED; } - + WindowState focus = (WindowState)focusObj; - + if (uid != 0 && uid != focus.mSession.mUid) { if (mContext.checkPermission( android.Manifest.permission.INJECT_EVENTS, pid, uid) @@ -4004,12 +4073,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.recycleEvent(qev); } ev.recycle(); - return false; + return INJECT_NO_PERMISSION; } } - + final long eventTime = ev.getEventTime(); - + synchronized(mWindowMap) { if (qev != null && ev.getAction() == MotionEvent.ACTION_MOVE) { mKeyWaiter.bindTargetWindowLocked(focus, @@ -4021,10 +4090,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mKeyWaiter.bindTargetWindowLocked(focus); } } - + try { focus.mClient.dispatchTrackball(ev, eventTime); - return true; + return INJECT_SUCCEEDED; } catch (android.os.RemoteException e) { Log.i(TAG, "WINDOW DIED during key dispatch: " + focus); try { @@ -4034,28 +4103,28 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // removed. } } - - return false; + + return INJECT_FAILED; } - + /** * @return Returns true if event was dispatched, false if it was dropped for any reason */ - private boolean dispatchKey(KeyEvent event, int pid, int uid) { + private int dispatchKey(KeyEvent event, int pid, int uid) { if (DEBUG_INPUT) Log.v(TAG, "Dispatch key: " + event); Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null, - null, false, false); + null, false, false, pid, uid); if (focusObj == null) { Log.w(TAG, "No focus window, dropping: " + event); - return false; + return INJECT_FAILED; } if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { - return true; + return INJECT_SUCCEEDED; } - + WindowState focus = (WindowState)focusObj; - + if (DEBUG_INPUT) Log.v( TAG, "Dispatching to " + focus + ": " + event); @@ -4066,10 +4135,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.w(TAG, "Permission denied: injecting key event from pid " + pid + " uid " + uid + " to window " + focus + " owned by uid " + focus.mSession.mUid); - return false; + return INJECT_NO_PERMISSION; } } - + synchronized(mWindowMap) { mKeyWaiter.bindTargetWindowLocked(focus); } @@ -4077,14 +4146,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // NOSHIP extra state logging mKeyWaiter.recordDispatchState(event, focus); // END NOSHIP - + try { if (DEBUG_INPUT || DEBUG_FOCUS) { Log.v(TAG, "Delivering key " + event.getKeyCode() + " to " + focus); } focus.mClient.dispatchKey(event); - return true; + return INJECT_SUCCEEDED; } catch (android.os.RemoteException e) { Log.i(TAG, "WINDOW DIED during key dispatch: " + focus); try { @@ -4094,14 +4163,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // removed. } } - - return false; + + return INJECT_FAILED; } - + public void pauseKeyDispatching(IBinder _token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "pauseKeyDispatching()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized (mWindowMap) { @@ -4115,7 +4184,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void resumeKeyDispatching(IBinder _token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "resumeKeyDispatching()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized (mWindowMap) { @@ -4129,18 +4198,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void setEventDispatching(boolean enabled) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "resumeKeyDispatching()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized (mWindowMap) { mKeyWaiter.setEventDispatchingLocked(enabled); } } - + /** * Injects a keystroke event into the UI. - * - * @param ev A motion event describing the keystroke action. (Be sure to use + * + * @param ev A motion event describing the keystroke action. (Be sure to use * {@link SystemClock#uptimeMillis()} as the timebase.) * @param sync If true, wait for the event to be completed before returning to the caller. * @return Returns true if event was dispatched, false if it was dropped for any reason @@ -4162,47 +4231,80 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState, deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM); - boolean result = dispatchKey(newEvent, Binder.getCallingPid(), Binder.getCallingUid()); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + final int result = dispatchKey(newEvent, pid, uid); if (sync) { - mKeyWaiter.waitForNextEventTarget(null, null, null, false, true); + mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid); } - return result; + Binder.restoreCallingIdentity(ident); + switch (result) { + case INJECT_NO_PERMISSION: + throw new SecurityException( + "Injecting to another application requires INJECT_EVENT permission"); + case INJECT_SUCCEEDED: + return true; + } + return false; } /** * Inject a pointer (touch) event into the UI. - * - * @param ev A motion event describing the pointer (touch) action. (As noted in - * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use + * + * @param ev A motion event describing the pointer (touch) action. (As noted in + * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use * {@link SystemClock#uptimeMillis()} as the timebase.) * @param sync If true, wait for the event to be completed before returning to the caller. * @return Returns true if event was dispatched, false if it was dropped for any reason */ public boolean injectPointerEvent(MotionEvent ev, boolean sync) { - boolean result = dispatchPointer(null, ev, Binder.getCallingPid(), Binder.getCallingUid()); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + final int result = dispatchPointer(null, ev, pid, uid); if (sync) { - mKeyWaiter.waitForNextEventTarget(null, null, null, false, true); + mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid); + } + Binder.restoreCallingIdentity(ident); + switch (result) { + case INJECT_NO_PERMISSION: + throw new SecurityException( + "Injecting to another application requires INJECT_EVENT permission"); + case INJECT_SUCCEEDED: + return true; } - return result; + return false; } - + /** * Inject a trackball (navigation device) event into the UI. - * - * @param ev A motion event describing the trackball action. (As noted in - * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use + * + * @param ev A motion event describing the trackball action. (As noted in + * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use * {@link SystemClock#uptimeMillis()} as the timebase.) * @param sync If true, wait for the event to be completed before returning to the caller. * @return Returns true if event was dispatched, false if it was dropped for any reason */ public boolean injectTrackballEvent(MotionEvent ev, boolean sync) { - boolean result = dispatchTrackball(null, ev, Binder.getCallingPid(), Binder.getCallingUid()); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + final int result = dispatchTrackball(null, ev, pid, uid); if (sync) { - mKeyWaiter.waitForNextEventTarget(null, null, null, false, true); + mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid); } - return result; + Binder.restoreCallingIdentity(ident); + switch (result) { + case INJECT_NO_PERMISSION: + throw new SecurityException( + "Injecting to another application requires INJECT_EVENT permission"); + case INJECT_SUCCEEDED: + return true; + } + return false; } - + private WindowState getFocusedWindow() { synchronized (mWindowMap) { return getFocusedWindowLocked(); @@ -4212,7 +4314,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private WindowState getFocusedWindowLocked() { return mCurrentFocus; } - + /** * This class holds the state for dispatching key events. This state * is protected by the KeyWaiter instance, NOT by the window lock. You @@ -4234,7 +4336,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private boolean wasFrozen; private boolean focusPaused; private WindowState curFocus; - + DispatchState(KeyEvent theEvent, WindowState theFocus) { focus = theFocus; event = theEvent; @@ -4256,7 +4358,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo focusPaused = theFocus.mToken.paused; } } - + public String toString() { return "{{" + event + " to " + focus + " @ " + time + " lw=" + lastWin + " lb=" + lastBinder @@ -4275,10 +4377,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public static final int RETURN_NOTHING = 0; public static final int RETURN_PENDING_POINTER = 1; public static final int RETURN_PENDING_TRACKBALL = 2; - + final Object SKIP_TARGET_TOKEN = new Object(); final Object CONSUMED_EVENT_TOKEN = new Object(); - + private WindowState mLastWin = null; private IBinder mLastBinder = null; private boolean mFinished = true; @@ -4286,10 +4388,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private boolean mEventDispatching = true; private long mTimeToSwitch = 0; /* package */ boolean mWasFrozen = false; - + // Target of Motion events WindowState mMotionTarget; - + // Windows above the target who would like to receive an "outside" // touch event for any down events outside of them. WindowState mOutsideTouchTargets; @@ -4301,7 +4403,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo */ Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev, MotionEvent nextMotion, boolean isPointerEvent, - boolean failIfTimeout) { + boolean failIfTimeout, int callingPid, int callingUid) { long startTime = SystemClock.uptimeMillis(); long keyDispatchingTimeout = 5 * 1000; long waitedFor = 0; @@ -4319,7 +4421,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo ", mLastWin=" + mLastWin); if (targetIsNew) { Object target = findTargetWindow(nextKey, qev, nextMotion, - isPointerEvent); + isPointerEvent, callingPid, callingUid); if (target == SKIP_TARGET_TOKEN) { // The user has pressed a special key, and we are // dropping all pending events before it. @@ -4334,9 +4436,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } targetWin = (WindowState)target; } - + AppWindowToken targetApp = null; - + // Now: is it okay to send the next event to this window? synchronized (this) { // First: did we come here based on the last window not @@ -4345,7 +4447,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!targetIsNew && mLastWin == null) { continue; } - + // We never dispatch events if not finished with the // last one, or the display is frozen. if (mFinished && !mDisplayFrozen) { @@ -4364,7 +4466,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (targetIsNew && !targetWin.mToken.paused) { return targetWin; } - + // If we didn't find a target window, and there is no // focused app window, then just eat the events. } else if (mFocusedApp == null) { @@ -4374,7 +4476,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return null; } } - + if (DEBUG_INPUT) Log.v( TAG, "Waiting for last key in " + mLastBinder + " target=" + targetWin @@ -4385,10 +4487,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + (targetWin != null ? targetWin.mToken.paused : false) + " mFocusedApp=" + mFocusedApp + " mCurrentFocus=" + mCurrentFocus); - + targetApp = targetWin != null ? targetWin.mAppToken : mFocusedApp; - + long curTimeout = keyDispatchingTimeout; if (mTimeToSwitch != 0) { long now = SystemClock.uptimeMillis(); @@ -4404,7 +4506,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo curTimeout = switchTimeout; } } - + try { // after that continue // processing keys, so we don't get stuck. @@ -4468,7 +4570,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo synchronized (this) { if (abort && (mLastWin == targetWin || targetWin == null)) { mFinished = true; - if (mLastWin != null) { + if (mLastWin != null) { if (DEBUG_INPUT) Log.v(TAG, "Window " + mLastWin + " timed out on key input"); @@ -4493,41 +4595,56 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev, - MotionEvent nextMotion, boolean isPointerEvent) { + MotionEvent nextMotion, boolean isPointerEvent, + int callingPid, int callingUid) { mOutsideTouchTargets = null; - + if (nextKey != null) { // Find the target window for a normal key event. final int keycode = nextKey.getKeyCode(); final int repeatCount = nextKey.getRepeatCount(); final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP; boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode); + if (!dispatch) { - mPolicy.interceptKeyTi(null, keycode, - nextKey.getMetaState(), down, repeatCount); + if (callingUid == 0 || + mContext.checkPermission( + android.Manifest.permission.INJECT_EVENTS, + callingPid, callingUid) + == PackageManager.PERMISSION_GRANTED) { + mPolicy.interceptKeyTi(null, keycode, + nextKey.getMetaState(), down, repeatCount); + } Log.w(TAG, "Event timeout during app switch: dropping " + nextKey); return SKIP_TARGET_TOKEN; } - + // System.out.println("##### [" + SystemClock.uptimeMillis() + "] WindowManagerService.dispatchKey(" + keycode + ", " + down + ", " + repeatCount + ")"); - + WindowState focus = null; synchronized(mWindowMap) { focus = getFocusedWindowLocked(); } - + wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); - - if (mPolicy.interceptKeyTi(focus, - keycode, nextKey.getMetaState(), down, repeatCount)) { - return CONSUMED_EVENT_TOKEN; + + if (callingUid == 0 || + (focus != null && callingUid == focus.mSession.mUid) || + mContext.checkPermission( + android.Manifest.permission.INJECT_EVENTS, + callingPid, callingUid) + == PackageManager.PERMISSION_GRANTED) { + if (mPolicy.interceptKeyTi(focus, + keycode, nextKey.getMetaState(), down, repeatCount)) { + return CONSUMED_EVENT_TOKEN; + } } - + return focus; - + } else if (!isPointerEvent) { boolean dispatch = mKeyWaiter.checkShouldDispatchKey(-1); if (!dispatch) { @@ -4535,20 +4652,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + nextMotion); return SKIP_TARGET_TOKEN; } - + WindowState focus = null; synchronized(mWindowMap) { focus = getFocusedWindowLocked(); } - + wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); return focus; } - + if (nextMotion == null) { return SKIP_TARGET_TOKEN; } - + boolean dispatch = mKeyWaiter.checkShouldDispatchKey( KeyEvent.KEYCODE_UNKNOWN); if (!dispatch) { @@ -4556,18 +4673,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + nextMotion); return SKIP_TARGET_TOKEN; } - + // Find the target window for a pointer event. int action = nextMotion.getAction(); final float xf = nextMotion.getX(); final float yf = nextMotion.getY(); final long eventTime = nextMotion.getEventTime(); - + final boolean screenWasOff = qev != null && (qev.flags&WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0; - + WindowState target = null; - + synchronized(mWindowMap) { synchronized (this) { if (action == MotionEvent.ACTION_DOWN) { @@ -4580,12 +4697,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + mMotionTarget); mMotionTarget = null; } - + // ACTION_DOWN is special, because we need to lock next events to // the window we'll land onto. final int x = (int)xf; final int y = (int)yf; - + final ArrayList windows = mWindows; final int N = windows.size(); WindowState topErrWindow = null; @@ -4646,7 +4763,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } break; } - + if ((flags & WindowManager.LayoutParams .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { child.mNextOutsideTouch = mOutsideTouchTargets; @@ -4663,18 +4780,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mMotionTarget = null; } } - + target = mMotionTarget; } } - + wakeupIfNeeded(target, eventType(nextMotion)); - + // Pointer events are a little different -- if there isn't a // target found for any event, then just drop it. return target != null ? target : SKIP_TARGET_TOKEN; } - + boolean checkShouldDispatchKey(int keycode) { synchronized (this) { if (mPolicy.isAppSwitchKeyTqTiLwLi(keycode)) { @@ -4688,14 +4805,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return true; } } - + void bindTargetWindowLocked(WindowState win, int pendingWhat, QueuedEvent pendingMotion) { synchronized (this) { bindTargetWindowLockedLocked(win, pendingWhat, pendingMotion); } } - + void bindTargetWindowLocked(WindowState win) { synchronized (this) { bindTargetWindowLockedLocked(win, RETURN_NOTHING, null); @@ -4713,7 +4830,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo releasePendingPointerLocked(s); s.mPendingPointerMove = pendingMotion; s.mPendingPointerWindow = win; - if (DEBUG_INPUT) Log.v(TAG, + if (DEBUG_INPUT) Log.v(TAG, "bindTargetToWindow " + s.mPendingPointerMove); } else if (pendingWhat == RETURN_PENDING_TRACKBALL) { releasePendingTrackballLocked(s); @@ -4722,7 +4839,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + void releasePendingPointerLocked(Session s) { if (DEBUG_INPUT) Log.v(TAG, "releasePendingPointer " + s.mPendingPointerMove); @@ -4731,14 +4848,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo s.mPendingPointerMove = null; } } - + void releasePendingTrackballLocked(Session s) { if (s.mPendingTrackballMove != null) { mQueue.recycleEvent(s.mPendingTrackballMove); s.mPendingTrackballMove = null; } } - + MotionEvent finishedKey(Session session, IWindow client, boolean force, int returnWhat) { if (DEBUG_INPUT) Log.v( @@ -4767,7 +4884,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo session.mPendingTrackballMove = null; session.mPendingTrackballWindow = null; } - + if (mLastBinder == client.asBinder()) { if (DEBUG_INPUT) Log.v( TAG, "finishedKey: last paused=" @@ -4783,7 +4900,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo notifyAll(); } } - + if (qev != null) { MotionEvent res = (MotionEvent)qev.event; if (DEBUG_INPUT) Log.v(TAG, @@ -4803,7 +4920,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo notifyAll(); } } - + void handleNewWindowLocked(WindowState newWindow) { if (!newWindow.canReceiveKeys()) { return; @@ -4904,7 +5021,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo notifyAll(); } } - + void appSwitchComing() { synchronized (this) { // Don't wait for more than .5 seconds for app to finish @@ -4917,13 +5034,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo notifyAll(); } } - + private final void doFinishedKeyLocked(boolean doRecycle) { if (mLastWin != null) { releasePendingPointerLocked(mLastWin.mSession); releasePendingTrackballLocked(mLastWin.mSession); } - + if (mLastWin == null || !mLastWin.mToken.paused || !mLastWin.isVisibleLw()) { // If the current window has been paused, we aren't -really- @@ -4939,7 +5056,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private class KeyQ extends KeyInputQueue implements KeyInputQueue.FilterCallback { PowerManager.WakeLock mHoldingScreen; - + KeyQ() { super(mContext); PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); @@ -4953,7 +5070,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mPolicy.preprocessInputEventTq(event)) { return true; } - + switch (event.type) { case RawInputEvent.EV_KEY: { // XXX begin hack @@ -4973,11 +5090,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } // XXX end hack - + boolean screenIsOff = !mPowerManager.screenIsOn(); boolean screenIsDim = !mPowerManager.screenIsBright(); int actions = mPolicy.interceptKeyTq(event, !screenIsOff); - + if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) { mPowerManager.goToSleep(event.when); } @@ -4992,7 +5109,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mPowerManager.userActivity(event.when, false, LocalPowerManager.BUTTON_EVENT, false); } - + if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) { if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) { filterQueue(this); @@ -5003,7 +5120,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return false; } } - + case RawInputEvent.EV_REL: { boolean screenIsOff = !mPowerManager.screenIsOn(); boolean screenIsDim = !mPowerManager.screenIsBright(); @@ -5020,7 +5137,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return true; } - + case RawInputEvent.EV_ABS: { boolean screenIsOff = !mPowerManager.screenIsOn(); boolean screenIsDim = !mPowerManager.screenIsBright(); @@ -5037,7 +5154,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return true; } - + default: return true; } @@ -5057,7 +5174,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return FILTER_KEEP; } } - + /** * Must be called with the main window manager lock held. */ @@ -5078,11 +5195,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mSafeMode = mPolicy.detectSafeMode(); return mSafeMode; } - + public void systemReady() { mPolicy.systemReady(); } - + private final class InputDispatcherThread extends Thread { // Time to wait when there is nothing to do: 9999 seconds. static final int LONG_WAIT=9999*1000; @@ -5090,7 +5207,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public InputDispatcherThread() { super("InputDispatcher"); } - + @Override public void run() { while (true) { @@ -5101,11 +5218,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + private void process() { android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); - + // The last key event we saw KeyEvent lastKey = null; @@ -5113,12 +5230,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo long lastKeyTime = SystemClock.uptimeMillis(); long nextKeyTime = lastKeyTime+LONG_WAIT; - // How many successive repeats we generated + // How many successive repeats we generated int keyRepeatCount = 0; // Need to report that configuration has changed? boolean configChanged = false; - + while (true) { long curTime = SystemClock.uptimeMillis(); @@ -5199,14 +5316,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.recycleEvent(ev); break; } - + } else if (configChanged) { configChanged = false; sendNewConfiguration(); - + } else if (lastKey != null) { curTime = SystemClock.uptimeMillis(); - + // Timeout occurred while key was down. If it is at or // past the key repeat time, dispatch the repeat. if (DEBUG_INPUT) Log.v( @@ -5215,7 +5332,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (curTime < nextKeyTime) { continue; } - + lastKeyTime = nextKeyTime; nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY; keyRepeatCount++; @@ -5223,14 +5340,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo TAG, "Key repeat: count=" + keyRepeatCount + ", next @ " + nextKeyTime); dispatchKey(KeyEvent.changeTimeRepeat(lastKey, curTime, keyRepeatCount), 0, 0); - + } else { curTime = SystemClock.uptimeMillis(); - + lastKeyTime = curTime; nextKeyTime = curTime + LONG_WAIT; } - + } catch (Exception e) { Log.e(TAG, "Input thread received uncaught exception: " + e, e); @@ -5253,14 +5370,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo SurfaceSession mSurfaceSession; int mNumWindow = 0; boolean mClientDead = false; - + /** * Current pointer move event being dispatched to client window... must * hold key lock to access. */ QueuedEvent mPendingPointerMove; WindowState mPendingPointerWindow; - + /** * Current trackball move event being dispatched to client window... must * hold key lock to access. @@ -5280,7 +5397,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo sb.append(mUid); sb.append("}"); mStringName = sb.toString(); - + synchronized (mWindowMap) { if (mInputMethodManager == null && mHaveInputMethods) { IBinder b = ServiceManager.getService( @@ -5311,7 +5428,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Binder.restoreCallingIdentity(ident); } } - + @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { @@ -5336,6 +5453,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RemoteException e) { } synchronized(mWindowMap) { + mClient.asBinder().unlinkToDeath(this, 0); mClientDead = true; killSessionLocked(); } @@ -5345,11 +5463,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int viewVisibility, Rect outContentInsets) { return addWindow(this, window, attrs, viewVisibility, outContentInsets); } - + public void remove(IWindow window) { removeWindow(this, window); } - + public int relayout(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, boolean insetsPending, Rect outFrame, Rect outContentInsets, @@ -5358,21 +5476,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo requestedWidth, requestedHeight, viewFlags, insetsPending, outFrame, outContentInsets, outVisibleInsets, outSurface); } - + public void setTransparentRegion(IWindow window, Region region) { setTransparentRegionWindow(this, window, region); } - + public void setInsets(IWindow window, int touchableInsets, Rect contentInsets, Rect visibleInsets) { setInsetsWindow(this, window, touchableInsets, contentInsets, visibleInsets); } - + public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { getWindowDisplayFrame(this, window, outDisplayFrame); } - + public void finishDrawing(IWindow window) { if (localLOGV) Log.v( TAG, "IWindow finishDrawing called for " + window); @@ -5392,7 +5510,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return mKeyWaiter.finishedKey(this, window, false, KeyWaiter.RETURN_PENDING_POINTER); } - + public MotionEvent getPendingTrackballMove(IWindow window) { if (localLOGV) Log.v( TAG, "IWindow getPendingMotionEvent called for " + window); @@ -5424,7 +5542,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + void windowAddedLocked() { if (mSurfaceSession == null) { if (localLOGV) Log.v( @@ -5439,7 +5557,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mNumWindow--; killSessionLocked(); } - + void killSessionLocked() { if (mNumWindow <= 0 && mClientDead) { mSessions.remove(this); @@ -5458,7 +5576,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow); pw.print(" mClientDead="); pw.print(mClientDead); @@ -5519,11 +5637,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean mHaveFrame; WindowState mNextOutsideTouch; - + // Actual frame shown on-screen (may be modified by animation) final Rect mShownFrame = new Rect(); final Rect mLastShownFrame = new Rect(); - + /** * Insets that determine the actually visible area */ @@ -5543,19 +5661,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * given internal insets before laying out other windows based on it. */ boolean mGivenInsetsPending; - + /** * These are the content insets that were given during layout for * this window, to be applied to windows behind it. */ final Rect mGivenContentInsets = new Rect(); - + /** * These are the visible insets that were given during layout for * this window, to be applied to windows behind it. */ final Rect mGivenVisibleInsets = new Rect(); - + /** * Flag indicating whether the touchable region should be adjusted by * the visible insets; if false the area outside the visible insets is @@ -5563,7 +5681,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * tests. */ int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; - + // Current transformation being applied. float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1; float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1; @@ -5602,7 +5720,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // where we don't yet have a surface, but should have one soon, so // we can give the window focus before waiting for the relayout. boolean mRelayoutCalled; - + // This is set after the Surface has been created but before the // window has been drawn. During this time the surface is hidden. boolean mDrawPending; @@ -5617,7 +5735,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // to delay showing the surface until all windows in a token are ready // to be shown. boolean mReadyToShow; - + // Set when the window has been shown in the screen the first time. boolean mHasDrawn; @@ -5626,17 +5744,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Currently on the mDestroySurface list? boolean mDestroying; - + // Completely remove from window manager after exit animation? boolean mRemoveOnExit; // Set when the orientation is changing and this window has not yet // been updated for the new orientation. boolean mOrientationChanging; - + // Is this window now (or just being) removed? boolean mRemoved; - + WindowState(Session s, IWindow c, WindowToken token, WindowState attachedWindow, WindowManager.LayoutParams a, int viewVisibility) { @@ -5662,7 +5780,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return; } mDeathRecipient = deathRecipient; - + if ((mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW)) { // The multiplier here is to reserve space for multiple @@ -5738,7 +5856,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo w = mAttrs.width == mAttrs.FILL_PARENT ? pw : mRequestedWidth; h = mAttrs.height== mAttrs.FILL_PARENT ? ph : mRequestedHeight; } - + final Rect container = mContainingFrame; container.set(pf); @@ -5747,12 +5865,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final Rect content = mContentFrame; content.set(cf); - + final Rect visible = mVisibleFrame; visible.set(vf); - + final Rect frame = mFrame; - + //System.out.println("In: w=" + w + " h=" + h + " container=" + // container + " x=" + mAttrs.x + " y=" + mAttrs.y); @@ -5764,7 +5882,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // 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; @@ -5775,19 +5893,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (visible.top < frame.top) visible.top = frame.top; if (visible.right > frame.right) visible.right = frame.right; if (visible.bottom > frame.bottom) visible.bottom = frame.bottom; - + final Rect contentInsets = mContentInsets; contentInsets.left = content.left-frame.left; contentInsets.top = content.top-frame.top; contentInsets.right = frame.right-content.right; contentInsets.bottom = frame.bottom-content.bottom; - + final Rect visibleInsets = mVisibleInsets; visibleInsets.left = visible.left-frame.left; visibleInsets.top = visible.top-frame.top; visibleInsets.right = frame.right-visible.right; visibleInsets.bottom = frame.bottom-visible.bottom; - + if (localLOGV) { //if ("com.google.android.youtube".equals(mAttrs.packageName) // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { @@ -5800,7 +5918,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo //} } } - + public Rect getFrameLw() { return mFrame; } @@ -5828,11 +5946,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public Rect getGivenContentInsetsLw() { return mGivenContentInsets; } - + public Rect getGivenVisibleInsetsLw() { return mGivenVisibleInsets; } - + public WindowManager.LayoutParams getAttrs() { return mAttrs; } @@ -5840,7 +5958,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public int getSurfaceLayer() { return mLayer; } - + public IApplicationToken getAppToken() { return mAppToken != null ? mAppToken.appToken : null; } @@ -5874,7 +5992,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mAnimation = null; } } - + Surface createSurfaceLocked() { if (mSurface == null) { mDrawPending = true; @@ -5914,7 +6032,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo try { mSurface = new Surface( - mSession.mSurfaceSession, mSession.mPid, + mSession.mSurfaceSession, mSession.mPid, 0, w, h, mAttrs.format, flags); } catch (Surface.OutOfResourcesException e) { Log.w(TAG, "OutOfResourcesException creating surface"); @@ -5924,7 +6042,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.e(TAG, "Exception creating surface", e); return null; } - + if (localLOGV) Log.v( TAG, "Got surface: " + mSurface + ", set left=" + mFrame.left + " top=" + mFrame.top @@ -5961,7 +6079,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return mSurface; } - + void destroySurfaceLocked() { // Window is no longer on-screen, so can no longer receive // key events... if we were waiting for it to finish @@ -5974,7 +6092,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mAppToken != null && this == mAppToken.startingWindow) { mAppToken.startingDisplayed = false; } - + if (localLOGV) Log.v( TAG, "Window " + this + " destroying surface " + mSurface + ", session " + mSession); @@ -6064,7 +6182,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo enableScreenIfNeededLocked(); applyEnterAnimationLocked(this); - + int i = mChildWindows.size(); while (i > 0) { i--; @@ -6074,7 +6192,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo c.performShowLocked(); } } - + if (mAttrs.type != TYPE_APPLICATION_STARTING && mAppToken != null) { mAppToken.firstWindowDrawn = true; @@ -6090,13 +6208,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return true; } - + // 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) { // We will run animations as long as the display isn't frozen. - + if (!mDrawPending && !mCommitDrawPending && mAnimation != null) { mHasTransformation = true; mHasLocalTransformation = true; @@ -6154,7 +6272,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mLocalAnimating = true; mAnimation = null; } - + if (!mAnimating && !mLocalAnimating) { return false; } @@ -6163,7 +6281,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo TAG, "Animation done in " + this + ": exiting=" + mExiting + ", reportedVisible=" + (mAppToken != null ? mAppToken.reportedVisible : false)); - + mAnimating = false; mLocalAnimating = false; mAnimation = null; @@ -6187,7 +6305,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mFinishedStarting.add(mAppToken); mH.sendEmptyMessage(H.FINISHED_STARTING); } - + finishExit(); if (mAppToken != null) { @@ -6203,16 +6321,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + ": exiting=" + mExiting + " remove=" + mRemoveOnExit + " windowAnimating=" + isWindowAnimating()); - + final int N = mChildWindows.size(); for (int i=0; i<N; i++) { ((WindowState)mChildWindows.get(i)).finishExit(); } - + if (!mExiting) { return; } - + if (isWindowAnimating()) { return; } @@ -6239,7 +6357,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mRemoveOnExit = false; } } - + boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { if (dsdx < .99999f || dsdx > 1.00001f) return false; if (dtdy < .99999f || dtdy > 1.00001f) return false; @@ -6247,7 +6365,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (dsdy < -.000001f || dsdy > .000001f) return false; return true; } - + void computeShownFrameLocked() { final boolean selfTransformation = mHasLocalTransformation; Transformation attachedTransformation = @@ -6258,7 +6376,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo ? mAppToken.transformation : null; if (selfTransformation || attachedTransformation != null || appTransformation != null) { - // cache often used attributes locally + // cache often used attributes locally final Rect frame = mFrame; final float tmpFloats[] = mTmpFloats; final Matrix tmpMatrix = mTmpMatrix; @@ -6280,7 +6398,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Here we must not transform the position of the surface // since it is already included in the transformation. //Log.i(TAG, "Transform: " + matrix); - + tmpMatrix.getValues(tmpFloats); mDsDx = tmpFloats[Matrix.MSCALE_X]; mDtDx = tmpFloats[Matrix.MSKEW_X]; @@ -6315,14 +6433,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { //Log.i(TAG, "Not applying alpha transform"); } - + if (localLOGV) Log.v( TAG, "Continuing animation in " + this + ": " + mShownFrame + ", alpha=" + mTransformation.getAlpha()); return; } - + mShownFrame.set(mFrame); mShownAlpha = mAlpha; mDsDx = 1; @@ -6330,7 +6448,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mDsDy = 0; mDtDy = 1; } - + /** * Is this window visible? It is not visible if there is no * surface, or we are in the process of running an exit animation @@ -6393,7 +6511,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo && (!mAttachedHidden || mAnimating); } } - + /** * Like isOnScreen(), but we don't return true if the window is part * of a transition that has not yet been started. @@ -6412,7 +6530,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final AppWindowToken atoken = mAppToken; return mAnimation != null || (attached != null && attached.mAnimation != null) - || (atoken != null && + || (atoken != null && (atoken.animation != null || atoken.inPendingTransaction)); } @@ -6454,7 +6572,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return false; } - + boolean isFullscreenOpaque(int screenWidth, int screenHeight) { if (mAttrs.format != PixelFormat.OPAQUE || mSurface == null || mAnimation != null || mDrawPending || mCommitDrawPending) { @@ -6546,7 +6664,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo 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); @@ -6662,7 +6780,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " " + mAttrs.getTitle() + " paused=" + mToken.paused + "}"; } } - + // ------------------------------------------------------------- // Window Token State // ------------------------------------------------------------- @@ -6673,17 +6791,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // The type of window this token is for, as per WindowManager.LayoutParams. final int windowType; - + // Set if this token was explicitly added by a client, so should // not be removed when all windows are removed. final boolean explicit; - + // For printing. String stringName; - + // If this is an AppWindowToken, this is non-null. AppWindowToken appWindowToken; - + // All of the windows associated with this token. final ArrayList<WindowState> windows = new ArrayList<WindowState>(); @@ -6734,7 +6852,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int groupId = -1; boolean appFullscreen; int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - + // These are used for determining when all windows associated with // an activity have been drawn, so they can be made visible together // at the same time. @@ -6743,20 +6861,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int numDrawnWindows; boolean inPendingTransaction; boolean allDrawn; - + // Is this token going to be hidden in a little while? If so, it // won't be taken into account for setting the screen orientation. boolean willBeHidden; - + // Is this window's surface needed? This is almost like hidden, except // it will sometimes be true a little earlier: when the token has // been shown, but is still waiting for its app transition to execute // before making its windows shown. boolean hiddenRequested; - + // Have we told the window clients to hide themselves? boolean clientHidden; - + // Last visibility state we reported to the app token. boolean reportedVisible; @@ -6765,16 +6883,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Have we been asked to have this token keep the screen frozen? boolean freezingScreen; - + boolean animating; Animation animation; boolean hasTransformation; final Transformation transformation = new Transformation(); - + // Offset to the window of all layers in the token, for use by // AppWindowToken animations. int animLayerAdjustment; - + // Information about an application starting window if displayed. StartingData startingData; WindowState startingWindow; @@ -6789,7 +6907,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo appWindowToken = this; appToken = _token; } - + public void setAnimation(Animation anim) { if (localLOGV) Log.v( TAG, "Setting animation in " + this + ": " + anim); @@ -6804,13 +6922,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else if (zorder == Animation.ZORDER_BOTTOM) { adj = -TYPE_LAYER_OFFSET; } - + if (animLayerAdjustment != adj) { animLayerAdjustment = adj; updateLayers(); } } - + public void setDummyAnimation() { if (animation == null) { if (localLOGV) Log.v( @@ -6825,7 +6943,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo animating = true; } } - + void updateLayers() { final int N = allAppWindows.size(); final int adj = animLayerAdjustment; @@ -6839,7 +6957,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + void sendAppVisibilityToClients() { final int N = allAppWindows.size(); for (int i=0; i<N; i++) { @@ -6856,7 +6974,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + void showAllWindowsLocked() { final int NW = allAppWindows.size(); for (int i=0; i<NW; i++) { @@ -6866,12 +6984,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo w.performShowLocked(); } } - + // This must be called while inside a transaction. boolean stepAnimationLocked(long currentTime, int dw, int dh) { if (!mDisplayFrozen) { // We will run animations as long as the display isn't frozen. - + if (animation == sDummyAnimation) { // This guy is going to animate, but not yet. For now count // it is not animating for purposes of scheduling transactions; @@ -6879,7 +6997,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // a real animation and the next call will execute normally. return false; } - + if ((allDrawn || animating || startingDisplayed) && animation != null) { if (!animating) { if (DEBUG_ANIM) Log.v( @@ -6915,7 +7033,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } hasTransformation = false; - + if (!animating) { return false; } @@ -6925,7 +7043,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) { moveInputMethodWindowsIfNeededLocked(true); } - + if (DEBUG_ANIM) Log.v( TAG, "Animation done in " + this + ": reportedVisible=" + reportedVisible); @@ -6935,13 +7053,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo animLayerAdjustment = 0; updateLayers(); } - + final int N = windows.size(); for (int i=0; i<N; i++) { ((WindowState)windows.get(i)).finishExit(); } updateReportedVisibilityLocked(); - + return false; } @@ -6949,11 +7067,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (appToken == null) { return; } - + int numInteresting = 0; int numVisible = 0; boolean nowGone = true; - + if (DEBUG_VISIBILITY) Log.v(TAG, "Update reported visibility: " + this); final int N = allAppWindows.size(); for (int i=0; i<N; i++) { @@ -6987,7 +7105,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo nowGone = false; } } - + boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting; if (DEBUG_VISIBILITY) Log.v(TAG, "VIS " + this + ": interesting=" + numInteresting + " visible=" + numVisible); @@ -7004,7 +7122,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mH.sendMessage(m); } } - + void dump(PrintWriter pw, String prefix) { super.dump(pw, prefix); if (appToken != null) { @@ -7069,7 +7187,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return stringName; } } - + public static WindowManager.LayoutParams findAnimations( ArrayList<AppWindowToken> order, ArrayList<AppWindowToken> tokenList1, @@ -7077,7 +7195,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // We need to figure out which animation to use... WindowManager.LayoutParams animParams = null; int animSrc = 0; - + //Log.i(TAG, "Looking for animations..."); for (int i=order.size()-1; i>=0; i--) { AppWindowToken wtoken = order.get(i); @@ -7106,10 +7224,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + return animParams; } - + // ------------------------------------------------------------- // DummyAnimation // ------------------------------------------------------------- @@ -7123,7 +7241,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } static final Animation sDummyAnimation = new DummyAnimation(); - + // ------------------------------------------------------------- // Async Handler // ------------------------------------------------------------- @@ -7134,7 +7252,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final CharSequence nonLocalizedLabel; final int labelRes; final int icon; - + StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel, int _labelRes, int _icon) { pkg = _pkg; @@ -7161,19 +7279,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public static final int ENABLE_SCREEN = 16; public static final int APP_FREEZE_TIMEOUT = 17; public static final int COMPUTE_AND_SEND_NEW_CONFIGURATION = 18; - + private Session mLastReportedHold; - + public H() { } - + @Override public void handleMessage(Message msg) { switch (msg.what) { case REPORT_FOCUS_CHANGE: { WindowState lastFocus; WindowState newFocus; - + synchronized(mWindowMap) { lastFocus = mLastFocus; newFocus = mCurrentFocus; @@ -7217,7 +7335,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo case REPORT_LOSING_FOCUS: { ArrayList<WindowState> losers; - + synchronized(mWindowMap) { losers = mLosingFocus; mLosingFocus = new ArrayList<WindowState>(); @@ -7249,10 +7367,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Animation has been canceled... do nothing. return; } - + if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Add starting " + wtoken + ": pkg=" + sd.pkg); - + View view = null; try { view = mPolicy.addStartingWindow( @@ -7379,7 +7497,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RemoteException ex) { } } break; - + case WINDOW_FREEZE_TIMEOUT: { synchronized (mWindowMap) { Log.w(TAG, "Window freeze timeout expired."); @@ -7396,7 +7514,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } break; } - + case HOLD_SCREEN_CHANGED: { Session oldHold; Session newHold; @@ -7405,7 +7523,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo newHold = (Session)msg.obj; mLastReportedHold = newHold; } - + if (oldHold != newHold) { try { if (oldHold != null) { @@ -7423,7 +7541,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } break; } - + case APP_TRANSITION_TIMEOUT: { synchronized (mWindowMap) { if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { @@ -7436,7 +7554,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } break; } - + case PERSIST_ANIMATION_SCALE: { Settings.System.putFloat(mContext.getContentResolver(), Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale); @@ -7444,7 +7562,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale); break; } - + case FORCE_GC: { synchronized(mWindowMap) { if (mAnimationPending) { @@ -7464,12 +7582,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Runtime.getRuntime().gc(); break; } - + case ENABLE_SCREEN: { performEnableScreen(); break; } - + case APP_FREEZE_TIMEOUT: { synchronized (mWindowMap) { Log.w(TAG, "App freeze timeout expired."); @@ -7485,14 +7603,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } break; } - + case COMPUTE_AND_SEND_NEW_CONFIGURATION: { - if (updateOrientationFromAppTokens(null, null) != null) { + if (updateOrientationFromAppTokensUnchecked(null, null) != null) { sendNewConfiguration(); } break; } - + } } } @@ -7526,7 +7644,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return false; } - + // ------------------------------------------------------------- // Internals // ------------------------------------------------------------- @@ -7534,7 +7652,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final WindowState windowForClientLocked(Session session, IWindow client) { return windowForClientLocked(session, client.asBinder()); } - + final WindowState windowForClientLocked(Session session, IBinder client) { WindowState win = mWindowMap.get(client); if (localLOGV) Log.v( @@ -7559,7 +7677,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int curBaseLayer = 0; int curLayer = 0; int i; - + for (i=0; i<N; i++) { WindowState w = (WindowState)mWindows.get(i); if (w.mBaseLayer == curBaseLayer || w.mIsImWindow) { @@ -7615,11 +7733,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + mInLayout = true; try { performLayoutAndPlaceSurfacesLockedInner(recoveringMemory); - + int i = mPendingRemove.size()-1; if (i >= 0) { while (i >= 0) { @@ -7655,7 +7773,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int i; // FIRST LOOP: Perform a layout, if needed. - + while (mLayoutNeeded) { mPolicy.beginLayoutLw(dw, dh); @@ -7690,7 +7808,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + // 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 @@ -7721,7 +7839,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + private final void performLayoutAndPlaceSurfacesLockedInner( boolean recoveringMemory) { final long currentTime = SystemClock.uptimeMillis(); @@ -7732,13 +7850,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int i; // FIRST LOOP: Perform a layout, if needed. - performLayoutLockedInner(); - + if (mFxSession == null) { mFxSession = new SurfaceSession(); } - + if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION"); // Initialize state of exiting tokens. @@ -7752,7 +7869,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } // SECOND LOOP: Execute animations and update visibility of windows. - boolean orientationChangeComplete = true; Session holdScreen = null; float screenBrightness = -1; @@ -8063,7 +8179,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo !w.mLastContentInsets.equals(w.mContentInsets); w.mVisibleInsetsChanged = !w.mLastVisibleInsets.equals(w.mVisibleInsets); - if (!w.mLastFrame.equals(w.mFrame) + if (!w.mLastFrame.equals(w.mFrame) || w.mContentInsetsChanged || w.mVisibleInsetsChanged) { w.mLastFrame.set(w.mFrame); @@ -8085,7 +8201,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo w.mAppToken.allDrawn = false; } } - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Log.v(TAG, "Resizing window " + w + " to " + w.mFrame); mResizingWindows.add(w); } else if (w.mOrientationChanging) { @@ -8275,7 +8391,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface + ": CREATE"); try { - mDimSurface = new Surface(mFxSession, 0, + mDimSurface = new Surface(mFxSession, 0, -1, 16, 16, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); @@ -8330,7 +8446,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " + mBlurSurface + ": CREATE"); try { - mBlurSurface = new Surface(mFxSession, 0, + mBlurSurface = new Surface(mFxSession, 0, -1, 16, 16, PixelFormat.OPAQUE, Surface.FX_SURFACE_BLUR); @@ -8384,7 +8500,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { more = false; } - + // Do we need to continue animating? if (more) { if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " @@ -8410,7 +8526,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + if (!blurring && mBlurShown) { if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " + mBlurSurface + ": HIDE"); @@ -8428,7 +8544,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } Surface.closeTransaction(); - + if (DEBUG_ORIENTATION && mDisplayFrozen) Log.v(TAG, "With display frozen, orientationChangeComplete=" + orientationChangeComplete); @@ -8441,7 +8557,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo stopFreezingDisplayLocked(); } } - + i = mResizingWindows.size(); if (i > 0) { do { @@ -8459,7 +8575,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } while (i > 0); mResizingWindows.clear(); } - + // Destroy the surface of any windows that are no longer visible. i = mDestroySurface.size(); if (i > 0) { @@ -8518,13 +8634,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mH.sendMessageDelayed(mH.obtainMessage(H.ANIMATE), delay); } } - + /** * Have the surface flinger show a surface, robustly dealing with * error conditions. In particular, if there is not enough memory * to show the surface, then we will try to get rid of other surfaces * in order to succeed. - * + * * @return Returns true if the surface was successfully shown. */ boolean showSurfaceRobustlyLocked(WindowState win) { @@ -8536,22 +8652,22 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RuntimeException e) { Log.w(TAG, "Failure showing surface " + win.mSurface + " in " + win); } - + reclaimSomeSurfaceMemoryLocked(win, "show"); - + return false; } - + void reclaimSomeSurfaceMemoryLocked(WindowState win, String operation) { final Surface surface = win.mSurface; - + EventLog.writeEvent(LOG_WM_NO_SURFACE_MEMORY, win.toString(), win.mSession.mPid, operation); - + if (mForceRemoves == null) { mForceRemoves = new ArrayList<WindowState>(); } - + long callingIdentity = Binder.clearCallingIdentity(); try { // There was some problem... first, do a sanity check of the @@ -8585,7 +8701,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + boolean killedApps = false; if (!leakedSurface) { Log.w(TAG, "No leaked surfaces; killing applicatons!"); @@ -8609,7 +8725,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + if (leakedSurface || killedApps) { // We managed to reclaim some memory, so get rid of the trouble // surface and ask the app to request another one. @@ -8618,7 +8734,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo surface.clear(); win.mSurface = null; } - + try { win.mClient.dispatchGetNewSurface(); } catch (RemoteException e) { @@ -8628,7 +8744,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Binder.restoreCallingIdentity(callingIdentity); } } - + private boolean updateFocusedWindowLocked(int mode) { WindowState newFocus = computeFocusedWindowLocked(); if (mCurrentFocus != newFocus) { @@ -8641,7 +8757,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; mLosingFocus.remove(newFocus); - + final WindowState imWindow = mInputMethodWindow; if (newFocus != imWindow && oldFocus != imWindow) { if (moveInputMethodWindowsIfNeededLocked( @@ -8657,7 +8773,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo assignLayersLocked(); } } - + if (newFocus != null && mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) { mKeyWaiter.handleNewWindowLocked(newFocus); } @@ -8685,13 +8801,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + ", canReceive=" + win.canReceiveKeys()); AppWindowToken thisApp = win.mAppToken; - + // If this window's application has been removed, just skip it. if (thisApp != null && thisApp.removed) { i--; continue; } - + // If there is a focused app, don't allow focus to go to any // windows below it. If this is an application window, step // through the app tokens until we find its app. @@ -8748,9 +8864,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return; } - + mScreenFrozenLock.acquire(); - + long now = SystemClock.uptimeMillis(); //Log.i(TAG, "Freezing, gc pending: " + mFreezeGcPending + ", now " + now); if (mFreezeGcPending != 0) { @@ -8763,32 +8879,32 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { mFreezeGcPending = now; } - + mDisplayFrozen = true; if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE; mAppTransitionReady = true; } - + if (PROFILE_ORIENTATION) { File file = new File("/data/system/frozen"); Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); } Surface.freezeDisplay(0); } - + private void stopFreezingDisplayLocked() { if (!mDisplayFrozen) { return; } - + mDisplayFrozen = false; mH.removeMessages(H.APP_FREEZE_TIMEOUT); if (PROFILE_ORIENTATION) { Debug.stopMethodTracing(); } Surface.unfreezeDisplay(0); - + // Reset the key delivery timeout on unfreeze, too. We force a wakeup here // too because regular key delivery processing should resume immediately. synchronized (mKeyWaiter) { @@ -8804,10 +8920,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mH.removeMessages(H.FORCE_GC); mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC), 2000); - + mScreenFrozenLock.release(); } - + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission("android.permission.DUMP") @@ -8817,7 +8933,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + ", uid=" + Binder.getCallingUid()); return; } - + synchronized(mWindowMap) { pw.println("Current Window Manager state:"); for (int i=mWindows.size()-1; i>=0; i--) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 9471eff..2fe4dd4 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -17,7 +17,7 @@ package com.android.server.am; import com.android.internal.os.BatteryStatsImpl; -import com.android.internal.os.RuntimeInit; +import com.android.server.AttributeCache; import com.android.server.IntentResolver; import com.android.server.ProcessMap; import com.android.server.ProcessStats; @@ -30,22 +30,25 @@ import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.ActivityThread; import android.app.AlertDialog; +import android.app.ApplicationErrorReport; import android.app.Dialog; import android.app.IActivityWatcher; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; -import android.app.IIntentReceiver; -import android.app.IIntentSender; import android.app.IServiceConnection; import android.app.IThumbnailReceiver; import android.app.Instrumentation; import android.app.PendingIntent; import android.app.ResultInfo; +import android.backup.IBackupManager; +import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentReceiver; +import android.content.IIntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; @@ -78,6 +81,9 @@ 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; @@ -92,10 +98,13 @@ 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.PrintWriter; import java.lang.IllegalStateException; import java.lang.ref.WeakReference; @@ -117,11 +126,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final boolean DEBUG_OOM_ADJ = localLOGV || false; static final boolean DEBUG_TRANSITION = localLOGV || false; static final boolean DEBUG_BROADCAST = localLOGV || false; + static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false; static final boolean DEBUG_SERVICE = localLOGV || false; static final boolean DEBUG_VISBILITY = localLOGV || false; static final boolean DEBUG_PROCESSES = localLOGV || false; static final boolean DEBUG_USER_LEAVING = localLOGV || false; static final boolean DEBUG_RESULTS = localLOGV || false; + static final boolean DEBUG_BACKUP = localLOGV || true; static final boolean VALIDATE_TOKENS = false; static final boolean SHOW_ACTIVITY_START_TIME = true; @@ -191,6 +202,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Maximum number of recent tasks that we can remember. static final int MAX_RECENT_TASKS = 20; + // Amount of time after a call to stopAppSwitches() during which we will + // prevent further untrusted switches from happening. + static final long APP_SWITCH_DELAY_TIME = 5*1000; + // How long until we reset a task when the user returns to it. Currently // 30 minutes. static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30; @@ -273,6 +288,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // because the user interacts with it so much. 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; + // 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. @@ -301,6 +320,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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; @@ -328,6 +348,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final ArrayList mHistory = new ArrayList(); /** + * Description of a request to start a new activity, which has been held + * due to app switches being disabled. + */ + class PendingActivityLaunch { + HistoryRecord r; + HistoryRecord sourceRecord; + Uri[] grantedUriPermissions; + int grantedMode; + boolean onlyIfNeeded; + } + + final ArrayList<PendingActivityLaunch> mPendingActivityLaunches + = new ArrayList<PendingActivityLaunch>(); + + /** * 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 @@ -605,6 +640,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = new ArrayList<ServiceRecord>(); /** + * Backup/restore process management + */ + String mBackupAppName = null; + BackupRecord mBackupTarget = null; + + /** * List of PendingThumbnailsRecord objects of clients who are still * waiting to receive all of the thumbnails for a task. */ @@ -704,6 +745,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int mFactoryTest; + boolean mCheckedForSetup; + + /** + * The time at which we will allow normal application switches again, + * after a call to {@link #stopAppSwitches()}. + */ + long mAppSwitchesAllowedTime; + + /** + * This is set to true after the first switch after mAppSwitchesAllowedTime + * is set; any switches after that will clear the time. + */ + boolean mDidAppSwitch; + /** * Set while we are wanting to sleep, to prevent any * activities from being started/resumed. @@ -757,6 +812,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ int[] mProcDeaths = new int[20]; + /** + * This is set if we had to do a delayed dexopt of an app before launching + * it, to increasing the ANR timeouts in that case. + */ + boolean mDidDexOpt; + String mDebugApp = null; boolean mWaitForDebugger = false; boolean mDebugTransient = false; @@ -852,6 +913,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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; AlertDialog mUidAlert; @@ -910,6 +972,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen d.show(); proc.anrDialog = d; } + + ensureScreenEnabled(); } break; case SHOW_FACTORY_ERROR_MSG: { Dialog d = new FactoryErrorDialog( @@ -952,6 +1016,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen processNextBroadcast(true); } break; case BROADCAST_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG); + mHandler.sendMessageDelayed(nmsg, BROADCAST_TIMEOUT); + return; + } broadcastTimeout(); } break; case PAUSE_TIMEOUT_MSG: { @@ -962,9 +1032,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen activityPaused(token, null, true); } break; case IDLE_TIMEOUT_MSG: { - IBinder token = (IBinder)msg.obj; + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT); + return; + } // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. + IBinder token = (IBinder)msg.obj; Log.w(TAG, "Activity idle timeout for " + token); activityIdleInternal(token, true); } break; @@ -980,6 +1057,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen activityIdle(token); } break; case SERVICE_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, SERVICE_TIMEOUT); + return; + } serviceTimeout((ProcessRecord)msg.obj); } break; case UPDATE_TIME_ZONE: { @@ -1016,6 +1100,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } break; case LAUNCH_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG); + mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT); + return; + } synchronized (ActivityManagerService.this) { if (mLaunchingActivity.isHeld()) { Log.w(TAG, "Launch timeout has expired, giving up wake lock!"); @@ -1036,11 +1126,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } case PROC_START_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT); + return; + } ProcessRecord app = (ProcessRecord)msg.obj; synchronized (ActivityManagerService.this) { processStartTimedOutLocked(app); } } + case DO_PENDING_ACTIVITY_LAUNCHES_MSG: { + synchronized (ActivityManagerService.this) { + doPendingActivityLaunchesLocked(true); + } + } } } }; @@ -1301,6 +1403,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ")); SECONDARY_SERVER_ADJ = Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ")); + 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 = @@ -1316,6 +1420,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE; SECONDARY_SERVER_MEM = Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; + 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 = @@ -1382,7 +1488,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (mProcessStatsThread) { final long now = SystemClock.uptimeMillis(); boolean haveNewCpuStats = false; - + if (MONITOR_CPU_USAGE && mLastCpuTime < (now-MONITOR_CPU_MIN_TIME)) { mLastCpuTime = now; @@ -1414,7 +1520,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - synchronized(mBatteryStatsService.getActiveStatistics()) { + final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics(); + synchronized(bstats) { synchronized(mPidsSelfLocked) { if (haveNewCpuStats) { if (mBatteryStatsService.isOnBattery()) { @@ -1426,12 +1533,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (pr != null) { BatteryStatsImpl.Uid.Proc ps = pr.batteryStats; ps.addCpuTimeLocked(st.rel_utime, st.rel_stime); + } else { + BatteryStatsImpl.Uid.Proc ps = + bstats.getProcessStatsLocked(st.name, st.pid); + if (ps != null) { + ps.addCpuTimeLocked(st.rel_utime, st.rel_stime); + } } } } } } - + if (mLastWriteTime < (now-BATTERY_STATS_TIME)) { mLastWriteTime = now; mBatteryStatsService.getActiveStatistics().writeLocked(); @@ -1495,6 +1608,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return null; } + private final HistoryRecord topRunningNonDelayedActivityLocked(HistoryRecord notTop) { + int i = mHistory.size()-1; + while (i >= 0) { + HistoryRecord r = (HistoryRecord)mHistory.get(i); + if (!r.finishing && !r.delayedResume && r != notTop) { + return r; + } + i--; + } + return null; + } + /** * This is a simplified version of topRunningActivityLocked that provides a number of * optional skip-over modes. It is intended for use with the ActivityWatcher hook only. @@ -1531,6 +1656,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return proc; } + private void ensurePackageDexOpt(String packageName) { + IPackageManager pm = ActivityThread.getPackageManager(); + try { + if (pm.performDexOpt(packageName)) { + mDidDexOpt = true; + } + } catch (RemoteException e) { + } + } + private boolean isNextTransitionForward() { int transit = mWindowManager.getPendingAppTransition(); return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN @@ -1590,6 +1725,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.isHomeActivity) { mHomeProcess = app; } + ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleLaunchActivity(new Intent(r.intent), r, r.info, r.icicle, results, newIntents, !andResume, isNextTransitionForward()); @@ -1640,6 +1776,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.stopped = true; } + // Launch the new version setup screen if needed. We do this -after- + // launching the initial activity (that is, home), so that it can have + // a chance to initialize itself while in the background, making the + // switch back to it faster and look better. + startSetupActivityLocked(); + return true; } @@ -1995,6 +2137,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (prev != null) { prev.resumeKeyDispatchingLocked(); } + + if (prev.app != null && prev.cpuTimeAtResume > 0 && mBatteryStatsService.isOnBattery()) { + long diff = 0; + synchronized (mProcessStatsThread) { + diff = mProcessStats.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume; + } + if (diff > 0) { + BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics(); + synchronized (bsi) { + BatteryStatsImpl.Uid.Proc ps = + bsi.getProcessStatsLocked(prev.info.applicationInfo.uid, + prev.info.packageName); + if (ps != null) { + ps.addForegroundTimeLocked(diff); + } + } + } + } + prev.cpuTimeAtResume = 0; // reset it } /** @@ -2027,6 +2188,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen next.resumeKeyDispatchingLocked(); ensureActivitiesVisibleLocked(null, 0); mWindowManager.executeAppTransition(); + + // Mark the point when the activity is resuming + // TODO: To be more accurate, the mark should be before the onCreate, + // not after the onResume. But for subsequent starts, onResume is fine. + if (next.app != null) { + synchronized (mProcessStatsThread) { + next.cpuTimeAtResume = mProcessStats.getCpuTimeForPid(next.app.pid); + } + } else { + next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process + } } /** @@ -2191,6 +2363,96 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + private boolean startHomeActivityLocked() { + if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL + && mTopAction == null) { + // We are running in factory test mode, but unable to find + // the factory test app, so just sit around displaying the + // error message and don't try to start anything. + return false; + } + Intent intent = new Intent( + mTopAction, + mTopData != null ? Uri.parse(mTopData) : null); + intent.setComponent(mTopComponent); + if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { + intent.addCategory(Intent.CATEGORY_HOME); + } + ActivityInfo aInfo = + intent.resolveActivityInfo(mContext.getPackageManager(), + STOCK_PM_FLAGS); + if (aInfo != null) { + intent.setComponent(new ComponentName( + aInfo.applicationInfo.packageName, aInfo.name)); + // Don't do this if the home app is currently being + // instrumented. + ProcessRecord app = getProcessRecordLocked(aInfo.processName, + aInfo.applicationInfo.uid); + if (app == null || app.instrumentationClass == null) { + intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); + startActivityLocked(null, intent, null, null, 0, aInfo, + null, null, 0, 0, 0, false, false); + } + } + + + return true; + } + + /** + * Starts the "new version setup screen" if appropriate. + */ + private void startSetupActivityLocked() { + // Only do this once per boot. + if (mCheckedForSetup) { + return; + } + + // We will show this screen if the current one is a different + // version than the last one shown, and we are not running in + // low-level factory test mode. + final ContentResolver resolver = mContext.getContentResolver(); + if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL && + Settings.Secure.getInt(resolver, + Settings.Secure.DEVICE_PROVISIONED, 0) != 0) { + mCheckedForSetup = true; + + // See if we should be showing the platform update setup UI. + Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP); + List<ResolveInfo> ris = mSelf.mContext.getPackageManager() + .queryIntentActivities(intent, PackageManager.GET_META_DATA); + + // We don't allow third party apps to replace this. + ResolveInfo ri = null; + for (int i=0; ris != null && i<ris.size(); i++) { + if ((ris.get(i).activityInfo.applicationInfo.flags + & ApplicationInfo.FLAG_SYSTEM) != 0) { + ri = ris.get(i); + break; + } + } + + if (ri != null) { + String vers = ri.activityInfo.metaData != null + ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION) + : null; + if (vers == null && ri.activityInfo.applicationInfo.metaData != null) { + vers = ri.activityInfo.applicationInfo.metaData.getString( + Intent.METADATA_SETUP_VERSION); + } + String lastVers = Settings.Secure.getString( + resolver, Settings.Secure.LAST_SETUP_SHOWN); + if (vers != null && !vers.equals(lastVers)) { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setComponent(new ComponentName( + ri.activityInfo.packageName, ri.activityInfo.name)); + startActivityLocked(null, intent, null, null, 0, ri.activityInfo, + null, null, 0, 0, 0, false, false); + } + } + } + } + /** * Ensure that the top activity in the stack is resumed. * @@ -2212,39 +2474,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (next == null) { // There are no more activities! Let's just start up the // Launcher... - if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL - && mTopAction == null) { - // We are running in factory test mode, but unable to find - // the factory test app, so just sit around displaying the - // error message and don't try to start anything. - return false; - } - Intent intent = new Intent( - mTopAction, - mTopData != null ? Uri.parse(mTopData) : null); - intent.setComponent(mTopComponent); - if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { - intent.addCategory(Intent.CATEGORY_HOME); - } - ActivityInfo aInfo = - intent.resolveActivityInfo(mContext.getPackageManager(), - STOCK_PM_FLAGS); - if (aInfo != null) { - intent.setComponent(new ComponentName( - aInfo.applicationInfo.packageName, aInfo.name)); - // Don't do this if the home app is currently being - // instrumented. - ProcessRecord app = getProcessRecordLocked(aInfo.processName, - aInfo.applicationInfo.uid); - if (app == null || app.instrumentationClass == null) { - intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivityLocked(null, intent, null, null, 0, aInfo, - null, null, 0, 0, 0, false, false); - } - } - return true; + return startHomeActivityLocked(); } + next.delayedResume = false; + // If the top activity is the resumed one, nothing to do. if (mResumedActivity == next && next.state == ActivityState.RESUMED) { // Make sure we have executed any pending transitions, since there @@ -2471,7 +2705,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return true; } - private final void startActivityLocked(HistoryRecord r, boolean newTask) { + private final void startActivityLocked(HistoryRecord r, boolean newTask, + boolean doResume) { final int NH = mHistory.size(); int addPos = -1; @@ -2558,7 +2793,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if ((r.intent.getFlags() &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { resetTaskIfNeededLocked(r, r); - doShow = topRunningActivityLocked(null) == r; + doShow = topRunningNonDelayedActivityLocked(null) == r; } } if (SHOW_APP_STARTING_ICON && doShow) { @@ -2588,13 +2823,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWindowManager.validateAppTokens(mHistory); } - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } } /** * Perform clear operation as requested by - * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: assuming the top task on the - * stack is the one that the new activity is being launched in, look for + * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the + * stack to the given task, then look for * an instance of that activity in the stack and, if found, finish all * activities on top of it and return the instance. * @@ -2602,9 +2839,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * @return Returns the old activity that should be continue to be used, * or null if none was found. */ - private final HistoryRecord performClearTopTaskLocked(int taskId, + private final HistoryRecord performClearTaskLocked(int taskId, HistoryRecord newR, boolean doClear) { int i = mHistory.size(); + + // First find the requested task. + while (i > 0) { + i--; + HistoryRecord r = (HistoryRecord)mHistory.get(i); + if (r.task.taskId == taskId) { + i++; + break; + } + } + + // Now clear it. while (i > 0) { i--; HistoryRecord r = (HistoryRecord)mHistory.get(i); @@ -2636,7 +2885,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // instance of the activity so a new fresh one can be started. if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE) { if (!ret.finishing) { - int index = indexOfTokenLocked(ret, false); + int index = indexOfTokenLocked(ret); if (index >= 0) { finishActivityLocked(ret, 0, Activity.RESULT_CANCELED, null, "clear"); @@ -2729,7 +2978,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord sourceRecord = null; HistoryRecord resultRecord = null; if (resultTo != null) { - int index = indexOfTokenLocked(resultTo, false); + int index = indexOfTokenLocked(resultTo); if (DEBUG_RESULTS) Log.v( TAG, "Sending result to " + resultTo + " (index " + index + ")"); if (index >= 0) { @@ -2840,15 +3089,75 @@ public final class ActivityManagerService extends ActivityManagerNative implemen intent, resolvedType, aInfo, mConfiguration, resultRecord, resultWho, requestCode, componentSpecified); - HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) - != 0 ? r : null; - + if (mResumedActivity == null + || mResumedActivity.info.applicationInfo.uid != callingUid) { + if (!checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) { + PendingActivityLaunch pal = new PendingActivityLaunch(); + pal.r = r; + pal.sourceRecord = sourceRecord; + pal.grantedUriPermissions = grantedUriPermissions; + pal.grantedMode = grantedMode; + pal.onlyIfNeeded = onlyIfNeeded; + mPendingActivityLaunches.add(pal); + return START_SWITCHES_CANCELED; + } + } + + if (mDidAppSwitch) { + // This is the second allowed switch since we stopped switches, + // so now just generally allow switches. Use case: user presses + // home (switches disabled, switch to home, mDidAppSwitch now true); + // user taps a home icon (coming from home so allowed, we hit here + // and now allow anyone to switch again). + mAppSwitchesAllowedTime = 0; + } else { + mDidAppSwitch = true; + } + + doPendingActivityLaunchesLocked(false); + + return startActivityUncheckedLocked(r, sourceRecord, + grantedUriPermissions, grantedMode, onlyIfNeeded, true); + } + + private final void doPendingActivityLaunchesLocked(boolean doResume) { + final int N = mPendingActivityLaunches.size(); + if (N <= 0) { + return; + } + for (int i=0; i<N; i++) { + PendingActivityLaunch pal = mPendingActivityLaunches.get(i); + startActivityUncheckedLocked(pal.r, pal.sourceRecord, + pal.grantedUriPermissions, pal.grantedMode, pal.onlyIfNeeded, + doResume && i == (N-1)); + } + mPendingActivityLaunches.clear(); + } + + private final int startActivityUncheckedLocked(HistoryRecord r, + HistoryRecord sourceRecord, Uri[] grantedUriPermissions, + int grantedMode, boolean onlyIfNeeded, boolean doResume) { + final Intent intent = r.intent; + final int callingUid = r.launchedFromUid; + + int launchFlags = intent.getFlags(); + // We'll invoke onUserLeaving before onPause only if the launching // activity did not explicitly state that this is an automated launch. mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; if (DEBUG_USER_LEAVING) Log.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving); + // If the caller has asked not to resume at this point, we make note + // of this in the record so that we can skip it when trying to find + // the top running activity. + if (!doResume) { + r.delayedResume = true; + } + + HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) + != 0 ? r : null; + // If the onlyIfNeeded flag is set, then we can do this if the activity // being launched is the same as the one making the call... or, as // a special case, if we do not know the caller then we count the @@ -2856,7 +3165,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (onlyIfNeeded) { HistoryRecord checkedCaller = sourceRecord; if (checkedCaller == null) { - checkedCaller = topRunningActivityLocked(notTop); + checkedCaller = topRunningNonDelayedActivityLocked(notTop); } if (!checkedCaller.realActivity.equals(r.realActivity)) { // Caller is not the same as launcher, so always needed. @@ -2894,7 +3203,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; } - if (resultRecord != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { // For whatever reason this activity is being launched into a new // task... yet the caller has requested a result back. Well, that // is pretty messed up, so instead immediately send back a cancel @@ -2902,10 +3211,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // dependency on its originator. Log.w(TAG, "Activity is launching as a new task, so cancelling activity result."); sendActivityResultLocked(-1, - resultRecord, resultWho, requestCode, + r.resultTo, r.resultWho, r.requestCode, Activity.RESULT_CANCELED, null); r.resultTo = null; - resultRecord = null; } boolean addingToTask = false; @@ -2916,7 +3224,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If bring to front is requested, and no result is requested, and // we can find a task that was started with this same // component, then instead of launching bring that one to the front. - if (resultRecord == null) { + if (r.resultTo == null) { // See if there is a task to bring to the front. If this is // a SINGLE_INSTANCE activity, there can be one and only one // instance of it in the history, and it is always in its own @@ -2938,7 +3246,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // to have the same behavior as if a new instance was // being started, which means not bringing it to the front // if the caller is not itself in the front. - HistoryRecord curTop = topRunningActivityLocked(notTop); + HistoryRecord curTop = topRunningNonDelayedActivityLocked(notTop); if (curTop.task != taskTop.task) { r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); boolean callerAtFront = sourceRecord == null @@ -2959,7 +3267,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // the client said not to do anything if that // is the case, so this is it! And for paranoia, make // sure we have correctly resumed the top activity. - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } return START_RETURN_INTENT_TO_CALLER; } if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0 @@ -2969,7 +3279,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // from the task up to the one being started. In most // cases this means we are resetting the task to its // initial state. - HistoryRecord top = performClearTopTaskLocked( + HistoryRecord top = performClearTaskLocked( taskTop.task.taskId, r, true); if (top != null) { if (top.frontOfTask) { @@ -3035,7 +3345,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // We didn't do anything... but it was needed (a.k.a., client // don't use that intent!) And for paranoia, make // sure we have correctly resumed the top activity. - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } return START_TASK_TO_FRONT; } } @@ -3052,8 +3364,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If the activity being launched is the same as the one currently // at the top, then we need to check if it should only be launched // once. - HistoryRecord top = topRunningActivityLocked(notTop); - if (top != null && resultRecord == null) { + HistoryRecord top = topRunningNonDelayedActivityLocked(notTop); + if (top != null && r.resultTo == null) { if (top.realActivity.equals(r.realActivity)) { if (top.app != null && top.app.thread != null) { if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 @@ -3062,7 +3374,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen logStartActivity(LOG_AM_NEW_INTENT, top, top.task); // For paranoia, make sure we have correctly // resumed the top activity. - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } if (onlyIfNeeded) { // We don't need to start a new activity, and // the client said not to do anything if that @@ -3077,9 +3391,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } else { - if (resultRecord != null) { + if (r.resultTo != null) { sendActivityResultLocked(-1, - resultRecord, resultWho, requestCode, + r.resultTo, r.resultWho, r.requestCode, Activity.RESULT_CANCELED, null); } return START_CLASS_NOT_FOUND; @@ -3088,7 +3402,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean newTask = false; // Should this be considered a new task? - if (resultRecord == null && !addingToTask + if (r.resultTo == null && !addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { // todo: should do better management of integers. mCurTask++; @@ -3108,14 +3422,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // In this case, we are adding the activity to an existing // task, but the caller has asked to clear that task if the // activity is already running. - HistoryRecord top = performClearTopTaskLocked( + HistoryRecord top = performClearTaskLocked( sourceRecord.task.taskId, r, true); if (top != null) { logStartActivity(LOG_AM_NEW_INTENT, r, top.task); deliverNewIntentLocked(top, r.intent); // For paranoia, make sure we have correctly // resumed the top activity. - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } return START_DELIVERED_TO_TOP; } } else if (!addingToTask && @@ -3128,7 +3444,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord top = moveActivityToFrontLocked(where); logStartActivity(LOG_AM_NEW_INTENT, r, top.task); deliverNewIntentLocked(top, r.intent); - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } return START_DELIVERED_TO_TOP; } } @@ -3157,7 +3475,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen EventLog.writeEvent(LOG_AM_CREATE_TASK, r.task.taskId); } logStartActivity(LOG_AM_CREATE_ACTIVITY, r, r.task); - startActivityLocked(r, newTask); + startActivityLocked(r, newTask, doResume); return START_SUCCESS; } @@ -3226,7 +3544,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized (this) { - int index = indexOfTokenLocked(callingActivity, false); + int index = indexOfTokenLocked(callingActivity); if (index < 0) { return false; } @@ -3376,7 +3694,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public void setRequestedOrientation(IBinder token, int requestedOrientation) { synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -3398,7 +3716,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public int getRequestedOrientation(IBinder token) { synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } @@ -3454,7 +3772,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen TAG, "Finishing activity: token=" + token + ", result=" + resultCode + ", data=" + resultData); - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return false; } @@ -3578,7 +3896,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final HistoryRecord finishCurrentActivityLocked(HistoryRecord r, int mode) { - final int index = indexOfTokenLocked(r, false); + final int index = indexOfTokenLocked(r); if (index < 0) { return null; } @@ -3703,7 +4021,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public final void finishSubActivity(IBinder token, String resultWho, int requestCode) { synchronized(this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -4253,7 +4571,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { - int index = indexOfTokenLocked(token, true); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -4523,6 +4841,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mPendingBroadcast = null; scheduleBroadcastsLocked(); } + if (mBackupTarget != null && mBackupTarget.app.pid == pid) { + Log.w(TAG, "Unattached app died before backup, skipping"); + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentDisconnected(app.info.packageName); + } catch (RemoteException e) { + // Can't happen; the backup manager is local + } + } } else { Log.w(TAG, "Spurious process start timeout - pid not known for " + app); } @@ -4587,6 +4915,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.thread = thread; app.curAdj = app.setAdj = -100; + app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT; app.forcingToForeground = null; app.foregroundServices = false; app.debugging = false; @@ -4610,11 +4939,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWaitForDebugger = mOrigWaitForDebugger; } } + // If the app is being launched for restore or full backup, set it up specially + boolean isRestrictedBackupMode = false; + if (mBackupTarget != null && mBackupAppName.equals(processName)) { + isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE) + || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL); + } + ensurePackageDexOpt(app.instrumentationInfo != null + ? app.instrumentationInfo.packageName + : app.info.packageName); + if (app.instrumentationClass != null) { + ensurePackageDexOpt(app.instrumentationClass.getPackageName()); + } thread.bindApplication(processName, app.instrumentationInfo != null ? app.instrumentationInfo : app.info, providers, app.instrumentationClass, app.instrumentationProfileFile, app.instrumentationArguments, app.instrumentationWatcher, testMode, - mConfiguration, getCommonServicesLocked()); + isRestrictedBackupMode, mConfiguration, getCommonServicesLocked()); updateLRUListLocked(app, false); app.lastRequestedGc = SystemClock.uptimeMillis(); } catch (Exception e) { @@ -4695,6 +5036,18 @@ 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); + ensurePackageDexOpt(mBackupTarget.appInfo.packageName); + try { + thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode); + } catch (Exception e) { + Log.w(TAG, "Exception scheduling backup agent creation: "); + e.printStackTrace(); + } + } + if (badApp) { // todo: Also need to kill application to deal with all // kinds of exceptions. @@ -4790,7 +5143,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // Get the activity record. - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); @@ -4911,6 +5264,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + final void ensureScreenEnabled() { + boolean enableScreen; + synchronized (this) { + enableScreen = !mBooted; + mBooted = true; + } + + if (enableScreen) { + EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN, + SystemClock.uptimeMillis()); + enableScreenAfterBoot(); + } + } + public final void activityPaused(IBinder token, Bundle icicle) { // Refuse possible leaked file descriptors if (icicle != null && icicle.hasFileDescriptors()) { @@ -4930,7 +5297,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord r = null; synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { r = (HistoryRecord)mHistory.get(index); if (!timeout) { @@ -4961,7 +5328,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { r = (HistoryRecord)mHistory.get(index); r.thumbnail = thumbnail; @@ -4991,7 +5358,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (this) { mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token); - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); if (r.state == ActivityState.DESTROYING) { @@ -5018,7 +5385,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private HistoryRecord getCallingRecordLocked(IBinder token) { - int index = indexOfTokenLocked(token, true); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); if (r != null) { @@ -5030,7 +5397,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public ComponentName getActivityClassForToken(IBinder token) { synchronized(this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); return r.intent.getComponent(); @@ -5041,7 +5408,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public String getPackageForToken(IBinder token) { synchronized(this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); return r.packageName; @@ -5080,7 +5447,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } HistoryRecord activity = null; if (type == INTENT_SENDER_ACTIVITY_RESULT) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return null; } @@ -6251,6 +6618,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen "moveTaskToFront()"); synchronized(this) { + if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), + Binder.getCallingUid(), "Task to front")) { + return; + } final long origId = Binder.clearCallingIdentity(); try { int N = mRecentTasks.size(); @@ -6335,6 +6706,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen "moveTaskToBack()"); synchronized(this) { + if (mResumedActivity != null && mResumedActivity.task.taskId == task) { + if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), + Binder.getCallingUid(), "Task to back")) { + return; + } + } final long origId = Binder.clearCallingIdentity(); moveTaskToBackLocked(task); Binder.restoreCallingIdentity(origId); @@ -6438,6 +6815,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen "moveTaskBackwards()"); synchronized(this) { + if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), + Binder.getCallingUid(), "Task backwards")) { + return; + } final long origId = Binder.clearCallingIdentity(); moveTaskBackwardsLocked(task); Binder.restoreCallingIdentity(origId); @@ -6587,7 +6968,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized(this) { if (r == null) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -6670,6 +7051,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } app.pubProviders.put(cpi.name, cpr); app.addPackage(cpi.applicationInfo.packageName); + ensurePackageDexOpt(cpi.applicationInfo.packageName); } } return providers; @@ -7179,6 +7561,55 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + public void stopAppSwitches() { + if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.STOP_APP_SWITCHES); + } + + synchronized(this) { + mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + + APP_SWITCH_DELAY_TIME; + mDidAppSwitch = false; + mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG); + Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG); + mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME); + } + } + + public void resumeAppSwitches() { + if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.STOP_APP_SWITCHES); + } + + synchronized(this) { + // Note that we don't execute any pending app switches... we will + // let those wait until either the timeout, or the next start + // activity request. + mAppSwitchesAllowedTime = 0; + } + } + + boolean checkAppSwitchAllowedLocked(int callingPid, int callingUid, + String name) { + if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) { + return true; + } + + final int perm = checkComponentPermission( + android.Manifest.permission.STOP_APP_SWITCHES, callingPid, + callingUid, -1); + if (perm == PackageManager.PERMISSION_GRANTED) { + return true; + } + + Log.w(TAG, name + " request from " + callingUid + " stopped"); + return false; + } + public void setDebugApp(String packageName, boolean waitForDebugger, boolean persistent) { enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP, @@ -7595,6 +8026,31 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return handleAppCrashLocked(app); } + private ComponentName getErrorReportReceiver(ProcessRecord app) { + IPackageManager pm = ActivityThread.getPackageManager(); + try { + // was an installer package name specified when this app was + // installed? + String installerPackageName = pm.getInstallerPackageName(app.info.packageName); + if (installerPackageName == null) { + return null; + } + + // is there an Activity in this package that handles ACTION_APP_ERROR? + Intent intent = new Intent(Intent.ACTION_APP_ERROR); + intent.setPackage(installerPackageName); + ResolveInfo info = pm.resolveIntent(intent, null, 0); + if (info == null || info.activityInfo == null) { + return null; + } + + return new ComponentName(installerPackageName, info.activityInfo.name); + } catch (RemoteException e) { + // will return null and no error report will be delivered + } + return null; + } + void makeAppNotRespondingLocked(ProcessRecord app, String tag, String shortMsg, String longMsg, byte[] crashData) { app.notResponding = true; @@ -7713,6 +8169,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } void startAppProblemLocked(ProcessRecord app) { + app.errorReportReceiver = getErrorReportReceiver(app); skipCurrentReceiverLocked(app); } @@ -7745,7 +8202,6 @@ 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; synchronized (this) { if (app != null) { @@ -7834,16 +8290,103 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int res = result.get(); + Intent appErrorIntent = null; synchronized (this) { if (r != null) { mProcessCrashTimes.put(r.info.processName, r.info.uid, SystemClock.uptimeMillis()); } + if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { + appErrorIntent = createAppErrorIntentLocked(r); + res = AppErrorDialog.FORCE_QUIT; + } + } + + if (appErrorIntent != null) { + try { + mContext.startActivity(appErrorIntent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "bug report receiver dissappeared", e); + } } return res; } + Intent createAppErrorIntentLocked(ProcessRecord r) { + ApplicationErrorReport report = createAppErrorReportLocked(r); + if (report == null) { + return null; + } + Intent result = new Intent(Intent.ACTION_APP_ERROR); + result.setComponent(r.errorReportReceiver); + result.putExtra(Intent.EXTRA_BUG_REPORT, report); + result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return result; + } + + ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r) { + if (r.errorReportReceiver == null) { + return null; + } + + if (!r.crashing && !r.notResponding) { + 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(); + } 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 + } + + return null; + } + public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() { // assume our apps are happy - lazy create the list List<ActivityManager.ProcessErrorStateInfo> errList = null; @@ -8475,9 +9018,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " #" + i + ":"); r.dump(pw, prefix + " "); } else if (inclOomAdj) { - pw.println(String.format("%s%s #%2d: oom_adj=%3d %s", + pw.println(String.format("%s%s #%2d: adj=%3d/%d %s", prefix, (r.persistent ? persistentLabel : normalLabel), - i, r.setAdj, r.toString())); + i, r.setAdj, r.setSchedGroup, r.toString())); } else { pw.println(String.format("%s%s #%2d: %s", prefix, (r.persistent ? persistentLabel : normalLabel), @@ -8540,7 +9083,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return false; } - private final int indexOfTokenLocked(IBinder token, boolean required) { + private final int indexOfTokenLocked(IBinder token) { int count = mHistory.size(); // convert the token to an entry in the history. @@ -8554,19 +9097,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen break; } } - if (index < 0 && required) { - RuntimeInit.crash(TAG, new InvalidTokenException(token)); - } return index; } - static class InvalidTokenException extends Exception { - InvalidTokenException(IBinder token) { - super("Bad activity token: " + token); - } - } - private final void killServicesLocked(ProcessRecord app, boolean allowRestart) { // Report disconnected services. @@ -8790,6 +9324,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.receivers.clear(); } + // 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"); + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentDisconnected(app.info.packageName); + } catch (RemoteException e) { + // can't happen; backup manager is local + } + } + // If the caller is restarting this app, then leave it in its // current lists and let the caller take care of it. if (restarting) { @@ -9130,6 +9676,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (r.stats.getBatteryStats()) { r.stats.startLaunchedLocked(); } + ensurePackageDexOpt(r.serviceInfo.packageName); app.thread.scheduleCreateService(r, r.serviceInfo); created = true; } finally { @@ -9579,7 +10126,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord activity = null; if (token != null) { - int aindex = indexOfTokenLocked(token, false); + int aindex = indexOfTokenLocked(token); if (aindex < 0) { Log.w(TAG, "Binding with unknown activity: " + token); return 0; @@ -9906,6 +10453,128 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // ========================================================= + // BACKUP AND RESTORE + // ========================================================= + + // Cause the target app to be launched if necessary and its backup agent + // 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); + enforceCallingPermission("android.permission.BACKUP", "startBackupAgent"); + + synchronized(this) { + // !!! TODO: currently no check here that we're already bound + BatteryStatsImpl.Uid.Pkg.Serv ss = null; + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + ss = stats.getServiceStatsLocked(app.uid, app.packageName, app.name); + } + + BackupRecord r = new BackupRecord(ss, app, backupMode); + ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName); + // startProcessLocked() returns existing proc's record if it's already running + ProcessRecord proc = startProcessLocked(app.processName, app, + false, 0, "backup", hostingName); + if (proc == null) { + Log.e(TAG, "Unable to start backup agent process " + r); + return false; + } + + r.app = proc; + mBackupTarget = r; + mBackupAppName = app.packageName; + + // Try not to kill the process during backup + updateOomAdjLocked(proc); + + // 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); + try { + proc.thread.scheduleCreateBackupAgent(app, backupMode); + } catch (RemoteException e) { + // !!! TODO: notify the backup manager that we crashed, or rely on + // death notices, or...? + } + } else { + if (DEBUG_BACKUP) Log.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 + // mBackupAppName describe the app, so that when it binds back to the AM we + // know that it's scheduled for a backup-agent operation. + } + + return true; + } + + // A backup agent has just come up + public void backupAgentCreated(String agentPackageName, IBinder agent) { + if (DEBUG_BACKUP) Log.v(TAG, "backupAgentCreated: " + agentPackageName + + " = " + agent); + + synchronized(this) { + if (!agentPackageName.equals(mBackupAppName)) { + Log.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!"); + return; + } + + long oldIdent = Binder.clearCallingIdentity(); + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentConnected(agentPackageName, agent); + } catch (RemoteException e) { + // can't happen; the backup manager service is local + } catch (Exception e) { + Log.w(TAG, "Exception trying to deliver BackupAgent binding: "); + e.printStackTrace(); + } finally { + Binder.restoreCallingIdentity(oldIdent); + } + } + } + + // done with this agent + public void unbindBackupAgent(ApplicationInfo appInfo) { + if (DEBUG_BACKUP) Log.v(TAG, "unbindBackupAgent: " + appInfo); + if (appInfo == null) { + Log.w(TAG, "unbind backup agent for null app"); + return; + } + + synchronized(this) { + if (mBackupAppName == null) { + Log.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"); + return; + } + + ProcessRecord proc = mBackupTarget.app; + mBackupTarget = null; + mBackupAppName = null; + + // Not backing this app up any more; reset its OOM adjustment + updateOomAdjLocked(proc); + + // If the app crashed during backup, 'thread' will be null here + if (proc.thread != null) { + try { + proc.thread.scheduleDestroyBackupAgent(appInfo); + } catch (Exception e) { + Log.e(TAG, "Exception when unbinding backup agent:"); + e.printStackTrace(); + } + } + } + } + // ========================================================= // BROADCASTS // ========================================================= @@ -10078,7 +10747,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean ordered, boolean sticky, int callingPid, int callingUid) { intent = new Intent(intent); - if (DEBUG_BROADCAST) Log.v( + if (DEBUG_BROADCAST_LIGHT) Log.v( TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent + " ordered=" + ordered); if ((resultTo != null) && !ordered) { @@ -10114,6 +10783,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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); + } } } } @@ -10176,8 +10849,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - final ContentResolver resolver = mContext.getContentResolver(); - // Figure out who all will receive this broadcast. List receivers = null; List<BroadcastFilter> registeredReceivers = null; @@ -10200,8 +10871,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ActivityThread.getPackageManager().queryIntentReceivers( intent, resolvedType, STOCK_PM_FLAGS); } - registeredReceivers = mReceiverResolver.queryIntent(resolver, - intent, resolvedType, false); + registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false); } } catch (RemoteException ex) { // pm is in same process, this will never happen. @@ -10573,9 +11243,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean started = false; try { - if (DEBUG_BROADCAST) Log.v(TAG, + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Delivering to component " + r.curComponent + ": " + r); + ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, r.resultCode, r.resultData, r.resultExtras, r.ordered); started = true; @@ -10643,12 +11314,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.curFilter = filter; filter.receiverList.curBroadcast = r; r.state = BroadcastRecord.CALL_IN_RECEIVE; + if (filter.receiverList.app != null) { + // Bump hosting application to no longer be in background + // scheduling class. Note that we can't do that if there + // isn't an app... but we can only be in that case for + // things that directly call the IActivityManager API, which + // are already core system stuff so don't matter for this. + r.curApp = filter.receiverList.app; + filter.receiverList.app.curReceiver = r; + updateOomAdjLocked(); + } } try { - if (DEBUG_BROADCAST) { + if (DEBUG_BROADCAST_LIGHT) { int seq = r.intent.getIntExtra("seq", -1); - Log.i(TAG, "Sending broadcast " + r.intent.getAction() + " seq=" + seq - + " app=" + filter.receiverList.app); + Log.i(TAG, "Delivering to " + filter.receiverList.app + + " (seq=" + seq + "): " + r); } performReceive(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, @@ -10662,6 +11343,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.receiver = null; r.curFilter = null; filter.receiverList.curBroadcast = null; + if (filter.receiverList.app != null) { + filter.receiverList.app.curReceiver = null; + } } } } @@ -10685,6 +11369,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen while (mParallelBroadcasts.size() > 0) { r = mParallelBroadcasts.remove(0); final int N = r.receivers.size(); + if (DEBUG_BROADCAST_LIGHT) Log.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, @@ -10692,6 +11378,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + target + ": " + r); deliverToRegisteredReceiver(r, (BroadcastFilter)target, false); } + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Done with parallel broadcast " + + r); } // Now take care of the next serialized one... @@ -10717,10 +11405,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + boolean looped = false; + do { if (mOrderedBroadcasts.size() == 0) { // No more broadcasts pending, so all done! scheduleAppGcsLocked(); + if (looped) { + // If we had finished the last ordered broadcast, then + // make sure all processes have correct oom and sched + // adjustments. + updateOomAdjLocked(); + } return; } r = mOrderedBroadcasts.get(0); @@ -10777,9 +11473,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (DEBUG_BROADCAST) Log.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG"); mHandler.removeMessages(BROADCAST_TIMEOUT_MSG); + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Finished with ordered broadcast " + + r); + // ... and on to the next... mOrderedBroadcasts.remove(0); r = null; + looped = true; continue; } } while (r == null); @@ -10793,6 +11493,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (recIdx == 0) { r.dispatchTime = r.startTime; + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Processing ordered broadcast " + + r); if (DEBUG_BROADCAST) Log.v(TAG, "Submitting BROADCAST_TIMEOUT_MSG for " + (r.startTime + BROADCAST_TIMEOUT)); @@ -11135,6 +11837,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, false, false, MY_PID, Process.SYSTEM_UID); + + AttributeCache ac = AttributeCache.instance(); + if (ac != null) { + ac.updateConfiguration(mConfiguration); + } } } @@ -11380,6 +12087,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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); + adj = BACKUP_APP_ADJ; + } + } + if (app.services.size() != 0 && adj > FOREGROUND_APP_ADJ) { // If this process has active services running in it, we would // like to avoid killing it unless it would prevent the current @@ -11506,7 +12221,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } app.curAdj = adj; - + app.curSchedGroup = (adj > VISIBLE_APP_ADJ && !app.persistent) + ? Process.THREAD_GROUP_BG_NONINTERACTIVE + : Process.THREAD_GROUP_DEFAULT; + return adj; } @@ -11651,6 +12369,32 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return false; } } + if (app.setSchedGroup != app.curSchedGroup) { + app.setSchedGroup = app.curSchedGroup; + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Log.v(TAG, + "Setting process group of " + app.processName + + " to " + app.curSchedGroup); + if (true) { + long oldId = Binder.clearCallingIdentity(); + try { + Process.setProcessGroup(app.pid, app.curSchedGroup); + } catch (Exception e) { + Log.w(TAG, "Failed setting process group of " + app.pid + + " to " + app.curSchedGroup); + e.printStackTrace(); + } finally { + Binder.restoreCallingIdentity(oldId); + } + } + if (false) { + if (app.thread != null) { + try { + app.thread.setSchedulingGroup(app.curSchedGroup); + } catch (RemoteException e) { + } + } + } + } } return true; @@ -11942,51 +12686,63 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } public boolean profileControl(String process, boolean start, - String path) throws RemoteException { + String path, ParcelFileDescriptor fd) throws RemoteException { - synchronized (this) { - // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to - // its own permission. - if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires permission " - + android.Manifest.permission.SET_ACTIVITY_WATCHER); - } - - ProcessRecord proc = null; - try { - int pid = Integer.parseInt(process); - synchronized (mPidsSelfLocked) { - proc = mPidsSelfLocked.get(pid); + try { + synchronized (this) { + // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to + // its own permission. + if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.SET_ACTIVITY_WATCHER); } - } catch (NumberFormatException e) { - } - - if (proc == null) { - HashMap<String, SparseArray<ProcessRecord>> all - = mProcessNames.getMap(); - SparseArray<ProcessRecord> procs = all.get(process); - if (procs != null && procs.size() > 0) { - proc = procs.valueAt(0); + + if (start && fd == null) { + throw new IllegalArgumentException("null fd"); } - } - - if (proc == null || proc.thread == null) { - throw new IllegalArgumentException("Unknown process: " + process); - } - - boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0")); - if (isSecure) { - if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { - throw new SecurityException("Process not debuggable: " + proc); + + ProcessRecord proc = null; + try { + int pid = Integer.parseInt(process); + synchronized (mPidsSelfLocked) { + proc = mPidsSelfLocked.get(pid); + } + } catch (NumberFormatException e) { + } + + if (proc == null) { + HashMap<String, SparseArray<ProcessRecord>> all + = mProcessNames.getMap(); + SparseArray<ProcessRecord> procs = all.get(process); + if (procs != null && procs.size() > 0) { + proc = procs.valueAt(0); + } + } + + if (proc == null || proc.thread == null) { + throw new IllegalArgumentException("Unknown process: " + process); + } + + boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0")); + if (isSecure) { + if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { + throw new SecurityException("Process not debuggable: " + proc); + } } - } - try { - proc.thread.profilerControl(start, path); + proc.thread.profilerControl(start, path, fd); + fd = null; return true; - } catch (RemoteException e) { - throw new IllegalStateException("Process disappeared"); + } + } catch (RemoteException e) { + throw new IllegalStateException("Process disappeared"); + } finally { + if (fd != null) { + try { + fd.close(); + } catch (IOException e) { + } } } } diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java index 3fcfad0..33894d6 100644 --- a/services/java/com/android/server/am/AppErrorDialog.java +++ b/services/java/com/android/server/am/AppErrorDialog.java @@ -19,17 +19,22 @@ package com.android.server.am; import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR; import android.content.Context; +import android.content.DialogInterface; import android.content.res.Resources; import android.os.Handler; import android.os.Message; +import android.util.Log; class AppErrorDialog extends BaseErrorDialog { + private final static String TAG = "AppErrorDialog"; + private final AppErrorResult mResult; private final ProcessRecord mProc; // Event 'what' codes static final int FORCE_QUIT = 0; static final int DEBUG = 1; + static final int FORCE_QUIT_AND_REPORT = 2; // 5-minute timeout, then we automatically dismiss the crash dialog static final long DISMISS_TIMEOUT = 1000 * 60 * 5; @@ -58,12 +63,22 @@ class AppErrorDialog extends BaseErrorDialog { setCancelable(false); - setButton(res.getText(com.android.internal.R.string.force_close), - mHandler.obtainMessage(FORCE_QUIT)); + setButton(DialogInterface.BUTTON_POSITIVE, + res.getText(com.android.internal.R.string.force_close), + mHandler.obtainMessage(FORCE_QUIT)); + if ((flags&1) != 0) { - setButton(res.getText(com.android.internal.R.string.debug), + 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), + mHandler.obtainMessage(FORCE_QUIT_AND_REPORT)); + } + setTitle(res.getText(com.android.internal.R.string.aerr_title)); getWindow().addFlags(FLAG_SYSTEM_ERROR); getWindow().setTitle("Application Error: " + app.info.processName); diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java index 7390ed0..03c2a04 100644 --- a/services/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/java/com/android/server/am/AppNotRespondingDialog.java @@ -18,7 +18,10 @@ package com.android.server.am; import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR; +import android.content.ActivityNotFoundException; import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; import android.content.res.Resources; import android.os.Handler; import android.os.Message; @@ -26,6 +29,13 @@ import android.os.Process; import android.util.Log; class AppNotRespondingDialog extends BaseErrorDialog { + private static final String TAG = "AppNotRespondingDialog"; + + // Event 'what' codes + static final int FORCE_CLOSE = 1; + static final int WAIT = 2; + static final int WAIT_AND_REPORT = 3; + private final ActivityManagerService mService; private final ProcessRecord mProc; @@ -67,10 +77,19 @@ class AppNotRespondingDialog extends BaseErrorDialog { ? res.getString(resid, name1.toString(), name2.toString()) : res.getString(resid, name1.toString())); - setButton(res.getText(com.android.internal.R.string.force_close), - mHandler.obtainMessage(1)); - setButton2(res.getText(com.android.internal.R.string.wait), - mHandler.obtainMessage(2)); + setButton(DialogInterface.BUTTON_POSITIVE, + res.getText(com.android.internal.R.string.force_close), + mHandler.obtainMessage(FORCE_CLOSE)); + setButton(DialogInterface.BUTTON_NEUTRAL, + res.getText(com.android.internal.R.string.wait), + mHandler.obtainMessage(WAIT)); + + if (app.errorReportReceiver != null) { + setButton(DialogInterface.BUTTON_NEGATIVE, + res.getText(com.android.internal.R.string.report), + mHandler.obtainMessage(WAIT_AND_REPORT)); + } + setTitle(res.getText(com.android.internal.R.string.anr_title)); getWindow().addFlags(FLAG_SYSTEM_ERROR); getWindow().setTitle("Application Not Responding: " + app.info.processName); @@ -81,16 +100,23 @@ class AppNotRespondingDialog extends BaseErrorDialog { private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { + Intent appErrorIntent = null; switch (msg.what) { - case 1: + case FORCE_CLOSE: // Kill the application. mService.killAppAtUsersRequest(mProc, AppNotRespondingDialog.this, true); break; - case 2: + case WAIT_AND_REPORT: + case WAIT: // Continue waiting for the application. synchronized (mService) { ProcessRecord app = mProc; + + if (msg.what == WAIT_AND_REPORT) { + appErrorIntent = mService.createAppErrorIntentLocked(app); + } + app.notResponding = false; app.notRespondingReport = null; if (app.anrDialog == AppNotRespondingDialog.this) { @@ -99,6 +125,14 @@ class AppNotRespondingDialog extends BaseErrorDialog { } break; } + + if (appErrorIntent != null) { + try { + getContext().startActivity(appErrorIntent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "bug report receiver dissappeared", e); + } + } } }; } diff --git a/services/java/com/android/server/am/BackupRecord.java b/services/java/com/android/server/am/BackupRecord.java new file mode 100644 index 0000000..5ac8e0d --- /dev/null +++ b/services/java/com/android/server/am/BackupRecord.java @@ -0,0 +1,57 @@ +/* + * 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.am; + +import com.android.internal.os.BatteryStatsImpl; + +import android.content.pm.ApplicationInfo; + +/** @hide */ +class BackupRecord { + // backup/restore modes + public static final int BACKUP_NORMAL = 0; + public static final int BACKUP_FULL = 1; + public static final int RESTORE = 2; + + final BatteryStatsImpl.Uid.Pkg.Serv stats; + String stringName; // cached toString() output + final ApplicationInfo appInfo; // information about BackupAgent's app + final int backupMode; // full backup / incremental / restore + ProcessRecord app; // where this agent is running or null + + // ----- Implementation ----- + + BackupRecord(BatteryStatsImpl.Uid.Pkg.Serv _agentStats, ApplicationInfo _appInfo, + int _backupMode) { + stats = _agentStats; + appInfo = _appInfo; + backupMode = _backupMode; + } + + public String toString() { + if (stringName != null) { + return stringName; + } + StringBuilder sb = new StringBuilder(128); + sb.append("BackupRecord{") + .append(Integer.toHexString(System.identityHashCode(this))) + .append(' ').append(appInfo.packageName) + .append(' ').append(appInfo.name) + .append(' ').append(appInfo.backupAgentName).append('}'); + return stringName = sb.toString(); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index 0387be5..39a1ee0 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -16,17 +16,19 @@ package com.android.server.am; -import com.android.internal.app.IBatteryStats; -import com.android.internal.os.BatteryStatsImpl; - import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.os.Process; import android.os.ServiceManager; +import android.telephony.SignalStrength; +import android.telephony.TelephonyManager; import android.util.Log; +import com.android.internal.app.IBatteryStats; +import com.android.internal.os.BatteryStatsImpl; + import java.io.FileDescriptor; import java.io.PrintWriter; @@ -177,10 +179,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } - public void notePhoneSignalStrength(int asu) { + public void notePhoneSignalStrength(SignalStrength signalStrength) { enforceCallingPermission(); synchronized (mStats) { - mStats.notePhoneSignalStrengthLocked(asu); + mStats.notePhoneSignalStrengthLocked(signalStrength); } } @@ -190,7 +192,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub { mStats.notePhoneDataConnectionStateLocked(dataType, hasData); } } - + + public void noteAirplaneMode(boolean airplaneMode) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteAirplaneModeLocked(airplaneMode); + } + } + public void noteWifiOn(int uid) { enforceCallingPermission(); synchronized (mStats) { @@ -205,6 +214,34 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } + public void noteStartAudio(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteAudioOnLocked(uid); + } + } + + public void noteStopAudio(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteAudioOffLocked(uid); + } + } + + public void noteStartVideo(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteVideoOnLocked(uid); + } + } + + public void noteStopVideo(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteVideoOffLocked(uid); + } + } + public void noteWifiRunning() { enforceCallingPermission(); synchronized (mStats) { diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index 4057ae8..da55049 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -16,7 +16,7 @@ package com.android.server.am; -import android.app.IIntentReceiver; +import android.content.IIntentReceiver; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java index 1488791..b3fc313 100644 --- a/services/java/com/android/server/am/HistoryRecord.java +++ b/services/java/com/android/server/am/HistoryRecord.java @@ -66,6 +66,7 @@ class HistoryRecord extends IApplicationToken.Stub { int theme; // resource identifier of activity's theme. TaskRecord task; // the task this is in. long startTime; // when we starting launching this activity + long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity Configuration configuration; // configuration activity was last running in HistoryRecord resultTo; // who started this entry, so will get our reply final String resultWho; // additional identifier for use by resultTo. @@ -85,6 +86,7 @@ class HistoryRecord extends IApplicationToken.Stub { boolean launchFailed; // set if a launched failed, to abort on 2nd try boolean haveState; // have we gotten the last activity state? boolean stopped; // is activity pause finished? + boolean delayedResume; // not yet resumed because of stopped app switches? boolean finishing; // activity in pending finish list? boolean configDestroy; // need to destroy due to config change? int configChangeFlags; // which config values have changed @@ -146,6 +148,7 @@ class HistoryRecord extends IApplicationToken.Stub { pw.print(" icicle="); pw.println(icicle); pw.print(prefix); pw.print("state="); pw.print(state); pw.print(" stopped="); pw.print(stopped); + pw.print(" delayedResume="); pw.print(delayedResume); pw.print(" finishing="); pw.println(finishing); pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused); pw.print(" inHistory="); pw.print(inHistory); @@ -191,6 +194,7 @@ class HistoryRecord extends IApplicationToken.Stub { launchFailed = false; haveState = false; stopped = false; + delayedResume = false; finishing = false; configDestroy = false; keysPaused = false; @@ -459,6 +463,12 @@ class HistoryRecord extends IApplicationToken.Stub { return false; } + if (service.mDidDexOpt) { + // Give more time since we were dexopting. + service.mDidDexOpt = false; + return false; + } + if (r.app.instrumentationClass == null) { service.appNotRespondingLocked(r.app, r, "keyDispatchingTimedOut"); } else { diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index 4381392..fa2a100 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -17,8 +17,8 @@ package com.android.server.am; import android.app.IActivityManager; -import android.app.IIntentSender; -import android.app.IIntentReceiver; +import android.content.IIntentSender; +import android.content.IIntentReceiver; import android.app.PendingIntent; import android.content.Intent; import android.os.Binder; diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 68aebc3..3f59710 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -56,6 +56,8 @@ class ProcessRecord implements Watchdog.PssRequestor { int setRawAdj; // Last set OOM unlimited adjustment for this process int curAdj; // Current OOM adjustment for this process int setAdj; // Last set OOM adjustment for this process + int curSchedGroup; // Currently desired scheduling class + int setSchedGroup; // Last set to background scheduling class boolean isForeground; // Is this app running the foreground UI? boolean setIsForeground; // Running foreground UI when last set? boolean foregroundServices; // Running any services that are foreground? @@ -107,6 +109,10 @@ class ProcessRecord implements Watchdog.PssRequestor { ActivityManager.ProcessErrorStateInfo crashingReport; ActivityManager.ProcessErrorStateInfo notRespondingReport; + // Who will be notified of the error. This is usually an activity in the + // app that installed the package. + ComponentName errorReportReceiver; + void dump(PrintWriter pw, String prefix) { if (info.className != null) { pw.print(prefix); pw.print("class="); pw.println(info.className); @@ -143,6 +149,8 @@ class ProcessRecord implements Watchdog.PssRequestor { pw.print(" setRaw="); pw.print(setRawAdj); pw.print(" cur="); pw.print(curAdj); pw.print(" set="); pw.println(setAdj); + pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup); + pw.print(" setSchedGroup="); pw.println(setSchedGroup); pw.print(prefix); pw.print("isForeground="); pw.print(isForeground); pw.print(" setIsForeground="); pw.print(setIsForeground); pw.print(" foregroundServices="); pw.print(foregroundServices); @@ -157,7 +165,14 @@ class ProcessRecord implements Watchdog.PssRequestor { pw.print(" "); pw.print(crashDialog); pw.print(" notResponding="); pw.print(notResponding); pw.print(" " ); pw.print(anrDialog); - pw.print(" bad="); pw.println(bad); + pw.print(" bad="); pw.print(bad); + + // crashing or notResponding is always set before errorReportReceiver + if (errorReportReceiver != null) { + pw.print(" errorReportReceiver="); + pw.print(errorReportReceiver.flattenToShortString()); + } + pw.println(); } if (activities.size() > 0) { pw.print(prefix); pw.print("activities="); pw.println(activities); diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java index 0facefc..32c24c6 100644 --- a/services/java/com/android/server/am/ReceiverList.java +++ b/services/java/com/android/server/am/ReceiverList.java @@ -16,7 +16,7 @@ package com.android.server.am; -import android.app.IIntentReceiver; +import android.content.IIntentReceiver; import android.content.Intent; import android.os.Binder; import android.os.Bundle; diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index 866334b..2d58659 100755 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -56,9 +56,9 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final String TAG = "UsageStats"; // Current on-disk Parcel version - private static final int VERSION = 1004; + private static final int VERSION = 1005; - private static final int CHECKIN_VERSION = 3; + private static final int CHECKIN_VERSION = 4; private static final String FILE_PREFIX = "usage-"; @@ -82,7 +82,9 @@ public final class UsageStatsService extends IUsageStats.Stub { // this lock held. final Object mFileLock; // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks - private String mResumedPkg; + private String mLastResumedPkg; + private String mLastResumedComp; + private boolean mIsResumed; private File mFile; private String mFileLeaf; //private File mBackupFile; @@ -92,11 +94,16 @@ public final class UsageStatsService extends IUsageStats.Stub { private int mLastWriteDay; static class TimeStats { + int count; int[] times = new int[NUM_LAUNCH_TIME_BINS]; TimeStats() { } + void incCount() { + count++; + } + void add(int val) { final int[] bins = LAUNCH_TIME_BINS; for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) { @@ -109,6 +116,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } TimeStats(Parcel in) { + count = in.readInt(); final int[] localTimes = times; for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { localTimes[i] = in.readInt(); @@ -116,6 +124,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } void writeToParcel(Parcel out) { + out.writeInt(count); final int[] localTimes = times; for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { out.writeInt(localTimes[i]); @@ -152,8 +161,10 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - void updateResume() { - mLaunchCount ++; + void updateResume(boolean launched) { + if (launched) { + mLaunchCount ++; + } mResumedTime = SystemClock.elapsedRealtime(); } @@ -162,6 +173,15 @@ public final class UsageStatsService extends IUsageStats.Stub { mUsageTime += (mPausedTime - mResumedTime); } + void addLaunchCount(String comp) { + TimeStats times = mLaunchTimes.get(comp); + if (times == null) { + times = new TimeStats(); + mLaunchTimes.put(comp, times); + } + times.incCount(); + } + void addLaunchTime(String comp, int millis) { TimeStats times = mLaunchTimes.get(comp); if (times == null) { @@ -436,43 +456,70 @@ public final class UsageStatsService extends IUsageStats.Stub { public void noteResumeComponent(ComponentName componentName) { enforceCallingPermission(); String pkgName; - if ((componentName == null) || - ((pkgName = componentName.getPackageName()) == null)) { - return; - } - if ((mResumedPkg != null) && (mResumedPkg.equalsIgnoreCase(pkgName))) { - // Moving across activities in same package. just return - return; - } - if (localLOGV) Log.i(TAG, "started component:"+pkgName); synchronized (mStatsLock) { + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + + final boolean samePackage = pkgName.equals(mLastResumedPkg); + if (mIsResumed) { + if (samePackage) { + Log.w(TAG, "Something wrong here, didn't expect " + + pkgName + " to be resumed"); + return; + } + + if (mLastResumedPkg != null) { + // We last resumed some other package... just pause it now + // to recover. + Log.w(TAG, "Unexpected resume of " + pkgName + + " while already resumed in " + mLastResumedPkg); + PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg); + if (pus != null) { + pus.updatePause(); + } + } + } + + final boolean sameComp = samePackage + && componentName.getClassName().equals(mLastResumedComp); + + mIsResumed = true; + mLastResumedPkg = pkgName; + mLastResumedComp = componentName.getClassName(); + + if (localLOGV) Log.i(TAG, "started component:" + pkgName); PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { pus = new PkgUsageStatsExtended(); mStats.put(pkgName, pus); } - pus.updateResume(); + pus.updateResume(!samePackage); + if (!sameComp) { + pus.addLaunchCount(mLastResumedComp); + } } - mResumedPkg = pkgName; } public void notePauseComponent(ComponentName componentName) { enforceCallingPermission(); - String pkgName; - if ((componentName == null) || - ((pkgName = componentName.getPackageName()) == null)) { - return; - } - if ((mResumedPkg == null) || (!pkgName.equalsIgnoreCase(mResumedPkg))) { - Log.w(TAG, "Something wrong here, Didn't expect "+pkgName+" to be paused"); - return; - } - if (localLOGV) Log.i(TAG, "paused component:"+pkgName); - - // Persist current data to file if needed. - writeStatsToFile(false); synchronized (mStatsLock) { + String pkgName; + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + if (!mIsResumed) { + Log.w(TAG, "Something wrong here, didn't expect " + + pkgName + " to be paused"); + return; + } + mIsResumed = false; + + if (localLOGV) Log.i(TAG, "paused component:"+pkgName); + PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { // Weird some error here @@ -481,6 +528,9 @@ public final class UsageStatsService extends IUsageStats.Stub { } pus.updatePause(); } + + // Persist current data to file if needed. + writeStatsToFile(false); } public void noteLaunchTime(ComponentName componentName, int millis) { @@ -631,9 +681,9 @@ public final class UsageStatsService extends IUsageStats.Stub { if (isCompactOutput) { sb.append("P:"); sb.append(pkgName); - sb.append(","); + sb.append(','); sb.append(pus.mLaunchCount); - sb.append(","); + sb.append(','); sb.append(pus.mUsageTime); sb.append('\n'); final int NC = pus.mLaunchTimes.size(); @@ -642,6 +692,8 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append("A:"); sb.append(ent.getKey()); TimeStats times = ent.getValue(); + sb.append(','); + sb.append(times.count); for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { sb.append(","); sb.append(times.times[i]); @@ -665,25 +717,26 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append(" "); sb.append(ent.getKey()); TimeStats times = ent.getValue(); + sb.append(": "); + sb.append(times.count); + sb.append(" starts"); int lastBin = 0; - boolean first = true; for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) { if (times.times[i] != 0) { - sb.append(first ? ": " : ", "); + sb.append(", "); sb.append(lastBin); sb.append('-'); sb.append(LAUNCH_TIME_BINS[i]); - sb.append('='); + sb.append("ms="); sb.append(times.times[i]); - first = false; } lastBin = LAUNCH_TIME_BINS[i]; } if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) { - sb.append(first ? ": " : ", "); + sb.append(", "); sb.append(">="); sb.append(lastBin); - sb.append('='); + sb.append("ms="); sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]); } sb.append('\n'); diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java index 0b161d6..7a8d4e5 100644 --- a/services/java/com/android/server/status/StatusBarPolicy.java +++ b/services/java/com/android/server/status/StatusBarPolicy.java @@ -41,6 +41,7 @@ import android.os.RemoteException; 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; @@ -57,6 +58,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.location.GpsLocationProvider; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.cdma.EriInfo; import com.android.internal.telephony.cdma.TtyIntent; import com.android.server.am.BatteryStatsService; @@ -97,7 +99,7 @@ public class StatusBarPolicy { private IBinder mBatteryIcon; private IconData mBatteryData; private boolean mBatteryFirst = true; - private boolean mBatteryPlugged; + private int mBatteryPlugged; private int mBatteryLevel; private int mBatteryThreshold = 0; // index into mBatteryThresholds private int[] mBatteryThresholds = new int[] { 20, 15, -1 }; @@ -106,50 +108,146 @@ public class StatusBarPolicy { private View mBatteryView; private int mBatteryViewSequence; private boolean mBatteryShowLowOnEndCall = false; + private boolean mSentLowBatteryBroadcast = false; private static final boolean SHOW_LOW_BATTERY_WARNING = true; // phone private TelephonyManager mPhone; private IBinder mPhoneIcon; + private IBinder mPhoneEvdoIcon; //***** Signal strength icons private IconData mPhoneData; + private IconData mPhoneEvdoData; //GSM/UMTS private static final int[] sSignalImages = new int[] { - com.android.internal.R.drawable.stat_sys_signal_0, - com.android.internal.R.drawable.stat_sys_signal_1, - com.android.internal.R.drawable.stat_sys_signal_2, - com.android.internal.R.drawable.stat_sys_signal_3, - com.android.internal.R.drawable.stat_sys_signal_4 - }; + com.android.internal.R.drawable.stat_sys_signal_0, + com.android.internal.R.drawable.stat_sys_signal_1, + com.android.internal.R.drawable.stat_sys_signal_2, + com.android.internal.R.drawable.stat_sys_signal_3, + com.android.internal.R.drawable.stat_sys_signal_4 + }; private static final int[] sSignalImages_r = new int[] { - com.android.internal.R.drawable.stat_sys_r_signal_0, - com.android.internal.R.drawable.stat_sys_r_signal_1, - com.android.internal.R.drawable.stat_sys_r_signal_2, - com.android.internal.R.drawable.stat_sys_r_signal_3, - com.android.internal.R.drawable.stat_sys_r_signal_4 - }; + com.android.internal.R.drawable.stat_sys_r_signal_0, + com.android.internal.R.drawable.stat_sys_r_signal_1, + com.android.internal.R.drawable.stat_sys_r_signal_2, + com.android.internal.R.drawable.stat_sys_r_signal_3, + com.android.internal.R.drawable.stat_sys_r_signal_4 + }; //CDMA private static final int[] sSignalImages_cdma = new int[] { - com.android.internal.R.drawable.stat_sys_signal_0_cdma, - com.android.internal.R.drawable.stat_sys_signal_1_cdma, - com.android.internal.R.drawable.stat_sys_signal_2_cdma, - com.android.internal.R.drawable.stat_sys_signal_3_cdma, - com.android.internal.R.drawable.stat_sys_signal_4_cdma + com.android.internal.R.drawable.stat_sys_signal_cdma_0, + com.android.internal.R.drawable.stat_sys_signal_cdma_1, + com.android.internal.R.drawable.stat_sys_signal_cdma_2, + com.android.internal.R.drawable.stat_sys_signal_cdma_3, + com.android.internal.R.drawable.stat_sys_signal_cdma_4 }; - private static final int[] sSignalImages_r_cdma = new int[] { - com.android.internal.R.drawable.stat_sys_r_signal_0_cdma, - com.android.internal.R.drawable.stat_sys_r_signal_1_cdma, - com.android.internal.R.drawable.stat_sys_r_signal_2_cdma, - com.android.internal.R.drawable.stat_sys_r_signal_3_cdma, - com.android.internal.R.drawable.stat_sys_r_signal_4_cdma + private static final int[] sRoamingIndicatorImages_cdma = new int[] { + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator + // 1 is Standard Roaming Indicator OFF + // TODO T: image never used, remove and put 0 instead? + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + + // 2 is Standard Roaming Indicator FLASHING + // TODO T: image never used, remove and put 0 instead? + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + + // 3-12 Standard ERI + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //3 + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + + // 13-63 Reserved for Standard ERI + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //13 + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + + // 64-127 Reserved for Non Standard (Operator Specific) ERI + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //64 + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0 //83 + + // 128-255 Reserved }; - private static final int[] sSignalImages_ra_cdma = new int[] { - com.android.internal.R.drawable.stat_sys_ra_signal_0_cdma, - com.android.internal.R.drawable.stat_sys_ra_signal_1_cdma, - com.android.internal.R.drawable.stat_sys_ra_signal_2_cdma, - com.android.internal.R.drawable.stat_sys_ra_signal_3_cdma, - com.android.internal.R.drawable.stat_sys_ra_signal_4_cdma + // EVDO + private static final int[] sSignalImages_evdo = new int[] { + com.android.internal.R.drawable.stat_sys_signal_evdo_0, + com.android.internal.R.drawable.stat_sys_signal_evdo_1, + com.android.internal.R.drawable.stat_sys_signal_evdo_2, + com.android.internal.R.drawable.stat_sys_signal_evdo_3, + com.android.internal.R.drawable.stat_sys_signal_evdo_4 }; //***** Data connection icons @@ -179,12 +277,14 @@ public class StatusBarPolicy { com.android.internal.R.drawable.stat_sys_data_in_evdo, com.android.internal.R.drawable.stat_sys_data_out_evdo, com.android.internal.R.drawable.stat_sys_data_inandout_evdo, + com.android.internal.R.drawable.stat_sys_data_dormant_evdo, }; private static final int[] sDataNetType_1xrtt = new int[] { com.android.internal.R.drawable.stat_sys_data_connected_1xrtt, com.android.internal.R.drawable.stat_sys_data_in_1xrtt, com.android.internal.R.drawable.stat_sys_data_out_1xrtt, com.android.internal.R.drawable.stat_sys_data_inandout_1xrtt, + com.android.internal.R.drawable.stat_sys_data_dormant_1xrtt, }; // Assume it's all good unless we hear otherwise. We don't always seem @@ -194,7 +294,7 @@ public class StatusBarPolicy { int mDataState = TelephonyManager.DATA_DISCONNECTED; int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; ServiceState mServiceState; - int mSignalAsu = -1; + SignalStrength mSignalStrength; // data connection private IBinder mDataIcon; @@ -249,6 +349,10 @@ public class StatusBarPolicy { private IBinder mTTYModeIcon; private IconData mTTYModeEnableIconData; + // Cdma Roaming Indicator, ERI + private IBinder mCdmaRoamingIndicatorIcon; + private IconData mCdmaRoamingIndicatorIconData; + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -306,6 +410,7 @@ public class StatusBarPolicy { private StatusBarPolicy(Context context, StatusBarService service) { mContext = context; mService = service; + mSignalStrength = new SignalStrength(); mBatteryStats = BatteryStatsService.getService(); // clock @@ -321,14 +426,21 @@ public class StatusBarPolicy { // phone_signal mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); - mPhoneData = IconData.makeIcon("phone_signal", + mPhoneData = IconData.makeIcon("phone_signal", null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0); mPhoneIcon = service.addIcon(mPhoneData, null); + + // phone_evdo_signal + mPhoneEvdoData = IconData.makeIcon("phone_evdo_signal", + null, com.android.internal.R.drawable.stat_sys_signal_evdo_0, 0, 0); + mPhoneEvdoIcon = service.addIcon(mPhoneEvdoData, null); + service.setIconVisibility(mPhoneEvdoIcon, false); + // register for phone state notifications. ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE)) .listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE - | PhoneStateListener.LISTEN_SIGNAL_STRENGTH + | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_CALL_STATE | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | PhoneStateListener.LISTEN_DATA_ACTIVITY); @@ -351,6 +463,12 @@ public class StatusBarPolicy { mTTYModeIcon = service.addIcon(mTTYModeEnableIconData, null); service.setIconVisibility(mTTYModeIcon, false); + // Cdma Roaming Indicator, ERI + mCdmaRoamingIndicatorIconData = IconData.makeIcon("cdma_eri", + null, com.android.internal.R.drawable.stat_sys_roaming_cdma_0, 0, 0); + mCdmaRoamingIndicatorIcon = service.addIcon(mCdmaRoamingIndicatorIconData, null); + service.setIconVisibility(mCdmaRoamingIndicatorIcon, false); + // bluetooth status mBluetoothData = IconData.makeIcon("bluetooth", null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0); @@ -464,7 +582,7 @@ public class StatusBarPolicy { mBatteryData.iconLevel = intent.getIntExtra("level", 0); mService.updateIcon(mBatteryIcon, mBatteryData, null); - boolean plugged = intent.getIntExtra("plugged", 0) != 0; + int plugged = intent.getIntExtra("plugged", 0); int level = intent.getIntExtra("level", -1); if (false) { Log.d(TAG, "updateBattery level=" + level @@ -475,7 +593,7 @@ public class StatusBarPolicy { + " mBatteryFirst=" + mBatteryFirst); } - boolean oldPlugged = mBatteryPlugged; + int oldPlugged = mBatteryPlugged; int oldThreshold = mBatteryThreshold; pickNextBatteryLevel(level); @@ -502,11 +620,12 @@ public class StatusBarPolicy { Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level + " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold); } - if (!plugged - && ((oldPlugged && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING]) + if (plugged == 0 + && ((oldPlugged != 0 && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING]) || (mBatteryThreshold > oldThreshold && mBatteryThreshold > BATTERY_THRESHOLD_WARNING))) { // Broadcast the low battery warning + mSentLowBatteryBroadcast = true; mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW)); if (SHOW_LOW_BATTERY_WARNING) { @@ -522,7 +641,11 @@ public class StatusBarPolicy { mBatteryShowLowOnEndCall = true; } } - } else if (mBatteryThreshold == BATTERY_THRESHOLD_CLOSE_WARNING) { + } else if (mBatteryThreshold < BATTERY_THRESHOLD_WARNING) { + if (mSentLowBatteryBroadcast == true) { + mSentLowBatteryBroadcast = false; + mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_OKAY)); + } if (SHOW_LOW_BATTERY_WARNING) { if (mLowBatteryDialog != null) { mLowBatteryDialog.dismiss(); @@ -611,6 +734,23 @@ public class StatusBarPolicy { b.setView(v); b.setIcon(android.R.drawable.ic_dialog_alert); b.setPositiveButton(android.R.string.ok, null); + + final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_MULTIPLE_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_NO_HISTORY); + if (intent.resolveActivity(mContext.getPackageManager()) != null) { + b.setNegativeButton(com.android.internal.R.string.battery_low_why, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mContext.startActivity(intent); + if (mLowBatteryDialog != null) { + mLowBatteryDialog.dismiss(); + } + } + }); + } AlertDialog d = b.create(); d.setOnDismissListener(mLowBatteryListener); @@ -629,7 +769,7 @@ public class StatusBarPolicy { } if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) { if (mBatteryShowLowOnEndCall) { - if (!mBatteryPlugged) { + if (mBatteryPlugged == 0) { showLowBatteryWarning(); } mBatteryShowLowOnEndCall = false; @@ -666,8 +806,8 @@ public class StatusBarPolicy { private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { @Override - public void onSignalStrengthChanged(int asu) { - mSignalAsu = asu; + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + mSignalStrength = signalStrength; updateSignalStrength(); } @@ -675,6 +815,7 @@ public class StatusBarPolicy { public void onServiceStateChanged(ServiceState state) { mServiceState = state; updateSignalStrength(); + updateCdmaRoamingIcon(); updateDataIcon(); } @@ -696,7 +837,6 @@ public class StatusBarPolicy { updateDataIcon(); } }; - private final void updateSimState(Intent intent) { String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE); @@ -723,25 +863,31 @@ public class StatusBarPolicy { updateDataIcon(); } - private final void updateSignalStrength() { - int asu = mSignalAsu; - ServiceState ss = mServiceState; + private boolean isCdma() { + return ((mPhone != null) && (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA)); + } - boolean hasService = true; - - if (ss != null) { - int state = ss.getState(); - switch (state) { + private boolean hasService() { + if (mServiceState != null) { + switch (mServiceState.getState()) { case ServiceState.STATE_OUT_OF_SERVICE: case ServiceState.STATE_POWER_OFF: - hasService = false; - break; + return false; + default: + return true; } } else { - hasService = false; + return false; } + } - if (!hasService) { + private final void updateSignalStrength() { + int iconLevel = -1; + int evdoIconLevel = -1; + int[] iconList; + int[] evdoIconList; + + if (!hasService()) { //Log.d(TAG, "updateSignalStrength: no service"); if (Settings.System.getInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1) { @@ -750,48 +896,92 @@ public class StatusBarPolicy { mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null; } mService.updateIcon(mPhoneIcon, mPhoneData, null); + mService.setIconVisibility(mPhoneEvdoIcon,false); return; } - // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5 - // asu = 0 (-113dB or less) is very weak - // signal, its better to show 0 bars to the user in such cases. - // asu = 99 is a special case, where the signal strength is unknown. - if (asu <= 0 || asu == 99) asu = 0; - else if (asu >= 16) asu = 4; - else if (asu >= 8) asu = 3; - else if (asu >= 4) asu = 2; - else asu = 1; - - int[] iconList; - if (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { - switch(ss.getExtendedCdmaRoaming()) { - case ServiceState.REGISTRATION_STATE_ROAMING: - iconList = this.sSignalImages_r_cdma; - break; - case ServiceState.REGISTRATION_STATE_ROAMING_AFFILIATE: - iconList = this.sSignalImages_ra_cdma; - break; - default: - iconList = this.sSignalImages_cdma; - break; + if (!isCdma()) { + int asu = mSignalStrength.getGsmSignalStrength(); + + // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5 + // asu = 0 (-113dB or less) is very weak + // signal, its better to show 0 bars to the user in such cases. + // asu = 99 is a special case, where the signal strength is unknown. + if (asu <= 0 || asu == 99) iconLevel = 0; + else if (asu >= 16) iconLevel = 4; + else if (asu >= 8) iconLevel = 3; + else if (asu >= 4) iconLevel = 2; + else iconLevel = 1; + + if (mPhone.isNetworkRoaming()) { + iconList = sSignalImages_r; + } else { + iconList = sSignalImages; } - } else if (mPhone.isNetworkRoaming()) { - iconList = sSignalImages_r; } else { - iconList = sSignalImages; + iconList = this.sSignalImages_cdma; + + int cdmaDbm = mSignalStrength.getCdmaDbm(); + int cdmaEcio = mSignalStrength.getCdmaEcio(); + int levelDbm = 0; + int levelEcio = 0; + + if (cdmaDbm >= -75) levelDbm = 4; + else if (cdmaDbm >= -85) levelDbm = 3; + else if (cdmaDbm >= -95) levelDbm = 2; + else if (cdmaDbm >= -100) levelDbm = 1; + else levelDbm = 0; + + // Ec/Io are in dB*10 + if (cdmaEcio >= -90) levelEcio = 4; + else if (cdmaEcio >= -110) levelEcio = 3; + else if (cdmaEcio >= -130) levelEcio = 2; + else if (cdmaEcio >= -150) levelEcio = 1; + else levelEcio = 0; + + iconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio; } - mPhoneData.iconId = iconList[asu]; + if ((mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_0) + || (mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_A)) { + // Use Evdo icon + evdoIconList = this.sSignalImages_evdo; + + int evdoEcio = mSignalStrength.getEvdoEcio(); + int evdoSnr = mSignalStrength.getEvdoSnr(); + int levelEvdoEcio = 0; + int levelEvdoSnr = 0; + + // Ec/Io are in dB*10 + if (evdoEcio >= -650) levelEvdoEcio = 4; + else if (evdoEcio >= -750) levelEvdoEcio = 3; + else if (evdoEcio >= -900) levelEvdoEcio = 2; + else if (evdoEcio >= -1050) levelEvdoEcio = 1; + else levelEvdoEcio = 0; + + if (evdoSnr > 7) levelEvdoSnr = 4; + else if (evdoSnr > 5) levelEvdoSnr = 3; + else if (evdoSnr > 3) levelEvdoSnr = 2; + else if (evdoSnr > 1) levelEvdoSnr = 1; + else levelEvdoSnr = 0; + + evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr; + + mPhoneEvdoData.iconId = evdoIconList[evdoIconLevel]; + mService.updateIcon(mPhoneEvdoIcon, mPhoneEvdoData, null); + mService.setIconVisibility(mPhoneEvdoIcon,true); + } else { + mService.setIconVisibility(mPhoneEvdoIcon,false); + } + + mPhoneData.iconId = iconList[iconLevel]; mService.updateIcon(mPhoneIcon, mPhoneData, null); } private final void updateDataNetType() { int net = mPhone.getNetworkType(); - ServiceState ss = this.mServiceState; switch (net) { - case TelephonyManager.NETWORK_TYPE_EDGE: mDataIconList = sDataNetType_e; break; @@ -819,32 +1009,51 @@ public class StatusBarPolicy { int iconId; boolean visible = true; - if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) { - int data = mDataState; - - int[] list = mDataIconList; - - ServiceState ss = mServiceState; - - boolean hasService = false; - - if (ss != null) { - hasService = (ss.getState() == ServiceState.STATE_IN_SERVICE); + if (!isCdma()) { + // GSM case, we have to check also the sim state + if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) { + if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { + switch (mDataActivity) { + case TelephonyManager.DATA_ACTIVITY_IN: + iconId = mDataIconList[1]; + break; + case TelephonyManager.DATA_ACTIVITY_OUT: + iconId = mDataIconList[2]; + break; + case TelephonyManager.DATA_ACTIVITY_INOUT: + iconId = mDataIconList[3]; + break; + default: + iconId = mDataIconList[0]; + break; + } + mDataData.iconId = iconId; + mService.updateIcon(mDataIcon, mDataData, null); + } else { + visible = false; + } + } else { + mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim; + mService.updateIcon(mDataIcon, mDataData, null); } - - if (hasService && data == TelephonyManager.DATA_CONNECTED) { + } else { + // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT + if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { switch (mDataActivity) { case TelephonyManager.DATA_ACTIVITY_IN: - iconId = list[1]; + iconId = mDataIconList[1]; break; case TelephonyManager.DATA_ACTIVITY_OUT: - iconId = list[2]; + iconId = mDataIconList[2]; break; case TelephonyManager.DATA_ACTIVITY_INOUT: - iconId = list[3]; + iconId = mDataIconList[3]; + break; + case TelephonyManager.DATA_ACTIVITY_DORMANT: + iconId = mDataIconList[4]; break; default: - iconId = list[0]; + iconId = mDataIconList[0]; break; } mDataData.iconId = iconId; @@ -852,10 +1061,8 @@ public class StatusBarPolicy { } else { visible = false; } - } else { - mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim; - mService.updateIcon(mDataIcon, mDataData, null); } + long ident = Binder.clearCallingIdentity(); try { mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible); @@ -863,6 +1070,7 @@ public class StatusBarPolicy { } finally { Binder.restoreCallingIdentity(ident); } + if (mDataIconVisible != visible) { mService.setIconVisibility(mDataIcon, visible); mDataIconVisible = visible; @@ -873,7 +1081,7 @@ public class StatusBarPolicy { AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); final int ringerMode = audioManager.getRingerMode(); final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT || - ringerMode == AudioManager.RINGER_MODE_VIBRATE; + ringerMode == AudioManager.RINGER_MODE_VIBRATE; final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER) ? com.android.internal.R.drawable.stat_sys_ringer_vibrate : com.android.internal.R.drawable.stat_sys_ringer_silent; @@ -905,7 +1113,7 @@ public class StatusBarPolicy { } else { return; } - + if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpState == BluetoothA2dp.STATE_CONNECTED || mBluetoothA2dpState == BluetoothA2dp.STATE_PLAYING) { @@ -920,15 +1128,15 @@ public class StatusBarPolicy { private final void updateWifi(Intent intent) { final String action = intent.getAction(); if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { - + final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; - + if (!enabled) { // If disabled, hide the icon. (We show icon when connected.) mService.setIconVisibility(mWifiIcon, false); } - + } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) { final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false); @@ -937,9 +1145,9 @@ public class StatusBarPolicy { } } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - final NetworkInfo networkInfo = (NetworkInfo) + final NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); - + int iconId; if (networkInfo != null && networkInfo.isConnected()) { mIsWifiConnected = true; @@ -986,18 +1194,18 @@ public class StatusBarPolicy { if (action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION) && enabled) { // GPS is getting fixes mService.updateIcon(mGpsIcon, mGpsFixIconData, null); - mService.setIconVisibility(mGpsIcon, true); + mService.setIconVisibility(mGpsIcon, true); } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) && !enabled) { // GPS is off - mService.setIconVisibility(mGpsIcon, false); + mService.setIconVisibility(mGpsIcon, false); } else { // GPS is on, but not receiving fixes mService.updateIcon(mGpsIcon, mGpsEnabledIconData, null); - mService.setIconVisibility(mGpsIcon, true); + mService.setIconVisibility(mGpsIcon, true); } } - private final void updateTTY(Intent intent) { + private final void updateTTY(Intent intent) { final String action = intent.getAction(); final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false); @@ -1007,14 +1215,63 @@ public class StatusBarPolicy { // TTY is on Log.i(TAG, "updateTTY: set TTY on"); mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null); - mService.setIconVisibility(mTTYModeIcon, true); + mService.setIconVisibility(mTTYModeIcon, true); } else { // TTY is off Log.i(TAG, "updateTTY: set TTY off"); - mService.setIconVisibility(mTTYModeIcon, false); + mService.setIconVisibility(mTTYModeIcon, false); } } + private final void updateCdmaRoamingIcon() { + if (!hasService()) { + mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false); + return; + } + + if (!isCdma()) { + mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false); + return; + } + + int[] iconList = sRoamingIndicatorImages_cdma; + int iconIndex = mPhone.getCdmaEriIconIndex(); + int iconMode = mPhone.getCdmaEriIconMode(); + + if (iconIndex == -1) { + Log.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update"); + return; + } + + if (iconMode == -1) { + Log.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update"); + return; + } + + if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) { + Log.d(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon"); + mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false); + return; + } + + switch (iconMode) { + case EriInfo.ROAMING_ICON_MODE_NORMAL: + mCdmaRoamingIndicatorIconData.iconId = iconList[iconIndex]; + mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null); + mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true); + break; + case EriInfo.ROAMING_ICON_MODE_FLASH: + mCdmaRoamingIndicatorIconData.iconId = + com.android.internal.R.drawable.stat_sys_roaming_cdma_flash; + mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null); + mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true); + break; + + } + mService.updateIcon(mPhoneIcon, mPhoneData, null); + } + + private class StatusBarHandler extends Handler { @Override public void handleMessage(Message msg) { @@ -1028,6 +1285,3 @@ public class StatusBarPolicy { } } } - - - diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java index 5336e27..b44168a 100644 --- a/services/java/com/android/server/status/StatusBarService.java +++ b/services/java/com/android/server/status/StatusBarService.java @@ -19,6 +19,7 @@ package com.android.server.status; import com.android.internal.R; import com.android.internal.util.CharSequences; +import android.app.ActivityManagerNative; import android.app.Dialog; import android.app.IStatusBar; import android.app.PendingIntent; @@ -118,6 +119,7 @@ public class StatusBarService extends IStatusBar.Stub public void binderDied() { Log.i(TAG, "binder died for pkg=" + pkg); disable(0, token, pkg); + token.unlinkToDeath(this, 0); } } @@ -493,6 +495,7 @@ public class StatusBarService extends IStatusBar.Stub if (what == 0 || !token.isBinderAlive()) { if (tok != null) { mDisableRecords.remove(i); + tok.token.unlinkToDeath(tok, 0); } } else { if (tok == null) { @@ -1254,6 +1257,14 @@ public class StatusBarService extends IStatusBar.Stub public void onClick(View v) { try { + // The intent we are sending is for the application, which + // won't have permission to immediately start an activity after + // the user switches to home. We know it is safe to do at this + // point, so make sure new activity switches are now allowed. + ActivityManagerNative.getDefault().resumeAppSwitches(); + } catch (RemoteException e) { + } + try { mIntent.send(); mNotificationCallbacks.onNotificationClick(mPkg, mId); } catch (PendingIntent.CanceledException e) { diff --git a/services/jni/com_android_server_SensorService.cpp b/services/jni/com_android_server_SensorService.cpp index 695a8a3..7390786 100644 --- a/services/jni/com_android_server_SensorService.cpp +++ b/services/jni/com_android_server_SensorService.cpp @@ -14,7 +14,10 @@ * limitations under the License. */ -#define LOG_TAG "Sensors" +#define LOG_TAG "SensorService" + +#define LOG_NDEBUG 0 +#include "utils/Log.h" #include <hardware/sensors.h> @@ -36,6 +39,14 @@ static struct parcel_file_descriptor_offsets_t jmethodID mConstructor; } gParcelFileDescriptorOffsets; +static struct bundle_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; + jmethodID mPutIntArray; + jmethodID mPutParcelableArray; +} gBundleOffsets; + /* * The method below are not thread-safe and not intended to be */ @@ -59,21 +70,45 @@ android_init(JNIEnv *env, jclass clazz) static jobject android_open(JNIEnv *env, jclass clazz) { - int fd = sSensorDevice->open_data_source(sSensorDevice); - // new FileDescriptor() - jobject filedescriptor = env->NewObject( - gFileDescriptorOffsets.mClass, - gFileDescriptorOffsets.mConstructor); - - if (filedescriptor != NULL) { - env->SetIntField(filedescriptor, gFileDescriptorOffsets.mDescriptor, fd); - // new ParcelFileDescriptor() - return env->NewObject(gParcelFileDescriptorOffsets.mClass, - gParcelFileDescriptorOffsets.mConstructor, - filedescriptor); + native_handle_t* handle = sSensorDevice->open_data_source(sSensorDevice); + if (!handle) { + return NULL; } - close(fd); - return NULL; + + // new Bundle() + jobject bundle = env->NewObject( + gBundleOffsets.mClass, + gBundleOffsets.mConstructor); + + if (handle->numFds > 0) { + jobjectArray fdArray = env->NewObjectArray(handle->numFds, + gParcelFileDescriptorOffsets.mClass, NULL); + for (int i = 0; i < handle->numFds; i++) { + // new FileDescriptor() + jobject fd = env->NewObject(gFileDescriptorOffsets.mClass, + gFileDescriptorOffsets.mConstructor); + env->SetIntField(fd, gFileDescriptorOffsets.mDescriptor, handle->data[i]); + // new ParcelFileDescriptor() + jobject pfd = env->NewObject(gParcelFileDescriptorOffsets.mClass, + gParcelFileDescriptorOffsets.mConstructor, fd); + env->SetObjectArrayElement(fdArray, i, pfd); + } + // bundle.putParcelableArray("fds", fdArray); + env->CallVoidMethod(bundle, gBundleOffsets.mPutParcelableArray, + env->NewStringUTF("fds"), fdArray); + } + + if (handle->numInts > 0) { + jintArray intArray = env->NewIntArray(handle->numInts); + env->SetIntArrayRegion(intArray, 0, handle->numInts, &handle->data[handle->numInts]); + // bundle.putIntArray("ints", intArray); + env->CallVoidMethod(bundle, gBundleOffsets.mPutIntArray, + env->NewStringUTF("ints"), intArray); + } + + // delete the file handle, but don't close any file descriptors + native_handle_delete(handle); + return bundle; } static jboolean @@ -99,7 +134,7 @@ android_data_wake(JNIEnv *env, jclass clazz) static JNINativeMethod gMethods[] = { {"_sensors_control_init", "()I", (void*) android_init }, - {"_sensors_control_open", "()Landroid/os/ParcelFileDescriptor;", (void*) android_open }, + {"_sensors_control_open", "()Landroid/os/Bundle;", (void*) android_open }, {"_sensors_control_activate", "(IZ)Z", (void*) android_activate }, {"_sensors_control_wake", "()I", (void*) android_data_wake }, {"_sensors_control_set_delay","(I)I", (void*) android_set_delay }, @@ -116,7 +151,15 @@ int register_android_server_SensorService(JNIEnv *env) clazz = env->FindClass("android/os/ParcelFileDescriptor"); gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); + gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", + "(Ljava/io/FileDescriptor;)V"); + + clazz = env->FindClass("android/os/Bundle"); + gBundleOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gBundleOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V"); + gBundleOffsets.mPutIntArray = env->GetMethodID(clazz, "putIntArray", "(Ljava/lang/String;[I)V"); + gBundleOffsets.mPutParcelableArray = env->GetMethodID(clazz, "putParcelableArray", + "(Ljava/lang/String;[Landroid/os/Parcelable;)V"); return jniRegisterNativeMethods(env, "com/android/server/SensorService", gMethods, NELEM(gMethods)); |