diff options
Diffstat (limited to 'services/java/com/android')
7 files changed, 356 insertions, 329 deletions
diff --git a/services/java/com/android/server/DeviceMemoryMonitor.java b/services/java/com/android/server/DeviceMemoryMonitor.java deleted file mode 100644 index 3669e1f..0000000 --- a/services/java/com/android/server/DeviceMemoryMonitor.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import com.android.server.am.ActivityManagerService; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.IPackageDataObserver; -import android.content.pm.IPackageManager; -import android.os.Binder; -import android.os.Handler; -import android.os.Message; -import android.os.Process; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.StatFs; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.provider.Settings.Gservices; -import android.util.Config; -import android.util.EventLog; -import android.util.Log; -import android.provider.Settings; - -/** - * This class implements a service to monitor the amount of disk storage space - * on the device. If the free storage on device is less than a tunable threshold value - * (default is 10%. this value is a gservices parameter) a low memory notification is - * displayed to alert the user. If the user clicks on the low memory notification the - * Application Manager application gets launched to let the user free storage space. - * Event log events: - * A low memory event with the free storage on device in bytes is logged to the event log - * when the device goes low on storage space. - * The amount of free storage on the device is periodically logged to the event log. The log - * interval is a gservices parameter with a default value of 12 hours - * When the free storage differential goes below a threshold(again a gservices parameter with - * a default value of 2MB), the free memory is logged to the event log - */ -class DeviceMemoryMonitorService extends Binder { - private static final String TAG = "DeviceMemoryMonitor"; - private static final boolean DEBUG = false; - private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; - private static final int DEVICE_MEMORY_WHAT = 1; - private static final int MONITOR_INTERVAL = 1; //in minutes - private static final int LOW_MEMORY_NOTIFICATION_ID = 1; - private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; - private static final int DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes - private static final int EVENT_LOG_STORAGE_BELOW_THRESHOLD = 2744; - private static final int EVENT_LOG_LOW_STORAGE_NOTIFICATION = 2745; - private static final int EVENT_LOG_FREE_STORAGE_LEFT = 2746; - private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB - private long mFreeMem; - private long mLastReportedFreeMem; - private long mLastReportedFreeMemTime; - private boolean mLowMemFlag=false; - private Context mContext; - private ContentResolver mContentResolver; - int mBlkSize; - long mTotalMemory; - StatFs mFileStats; - private static final String DATA_PATH="/data"; - long mThreadStartTime = -1; - boolean mClearSucceeded = false; - boolean mClearingCache; - private Intent mStorageLowIntent; - private Intent mStorageOkIntent; - /** - * Handler that checks the amount of disk space on the device and sends a - * notification if the device runs low on disk space - */ - Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - //dont handle an invalid message - if (msg.what != DEVICE_MEMORY_WHAT) { - Log.e(TAG, "Will not process invalid message"); - return; - } - checkMemory(); - } - }; - - class CachePackageDataObserver extends IPackageDataObserver.Stub { - public void onRemoveCompleted(String packageName, boolean succeeded) { - mClearSucceeded = succeeded; - mClearingCache = false; - if(localLOGV) Log.i(TAG, " Clear succeeded:"+mClearSucceeded - +", mClearingCache:"+mClearingCache); - } - } - - private final void restatDataDir() { - mFileStats.restat(DATA_PATH); - mFreeMem = mFileStats.getAvailableBlocks()*mBlkSize; - // Allow freemem to be overridden by debug.freemem for testing - String debugFreeMem = SystemProperties.get("debug.freemem"); - if (!"".equals(debugFreeMem)) { - mFreeMem = Long.parseLong(debugFreeMem); - } - // Read the log interval from Gservices - long freeMemLogInterval = Gservices.getLong(mContentResolver, - Gservices.SYS_FREE_STORAGE_LOG_INTERVAL, - DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000; - //log the amount of free memory in event log - long currTime = SystemClock.elapsedRealtime(); - if((mLastReportedFreeMemTime == 0) || - (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) { - mLastReportedFreeMemTime = currTime; - EventLog.writeEvent(EVENT_LOG_FREE_STORAGE_LEFT, mFreeMem); - } - // Read the reporting threshold from Gservices - long threshold = Gservices.getLong(mContentResolver, - Gservices.DISK_FREE_CHANGE_REPORTING_THRESHOLD, - DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD); - // If mFree changed significantly log the new value - long delta = mFreeMem - mLastReportedFreeMem; - if (delta > threshold || delta < -threshold) { - mLastReportedFreeMem = mFreeMem; - EventLog.writeEvent(EVENT_LOG_STORAGE_BELOW_THRESHOLD, mFreeMem); - } - } - - private final void clearCache() { - CachePackageDataObserver observer = new CachePackageDataObserver(); - mClearingCache = true; - try { - IPackageManager.Stub.asInterface(ServiceManager.getService("package")). - freeApplicationCache(getMemThreshold(), observer); - } catch (RemoteException e) { - Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e); - mClearingCache = false; - mClearSucceeded = false; - } - } - - private final void checkMemory() { - //if the thread that was started to clear cache is still running do nothing till its - //finished clearing cache. Ideally this flag could be modified by clearCache - // and should be accessed via a lock but even if it does this test will fail now and - //hopefully the next time this flag will be set to the correct value. - if(mClearingCache) { - if(localLOGV) Log.i(TAG, "Thread already running just skip"); - //make sure the thread is not hung for too long - long diffTime = System.currentTimeMillis() - mThreadStartTime; - if(diffTime > (10*60*1000)) { - Log.w(TAG, "Thread that clears cache file seems to run for ever"); - } - } else { - restatDataDir(); - if (localLOGV) Log.v(TAG, "freeMemory="+mFreeMem); - //post intent to NotificationManager to display icon if necessary - long memThreshold = getMemThreshold(); - if (mFreeMem < memThreshold) { - if (!mLowMemFlag) { - //see if clearing cache helps - mThreadStartTime = System.currentTimeMillis(); - clearCache(); - Log.i(TAG, "Running low on memory. Sending notification"); - sendNotification(); - mLowMemFlag = true; - } else { - if (localLOGV) Log.v(TAG, "Running low on memory " + - "notification already sent. do nothing"); - } - } else { - if (mLowMemFlag) { - Log.i(TAG, "Memory available. Cancelling notification"); - cancelNotification(); - mLowMemFlag = false; - } - } - } - if(localLOGV) Log.i(TAG, "Posting Message again"); - //keep posting messages to itself periodically - mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT), - MONITOR_INTERVAL*60*1000); - } - - /* - * just query settings to retrieve the memory threshold. - * Preferred this over using a ContentObserver since Settings.Gservices caches the value - * any way - */ - private long getMemThreshold() { - int value = Settings.Gservices.getInt( - mContentResolver, - Settings.Gservices.SYS_STORAGE_THRESHOLD_PERCENTAGE, - DEFAULT_THRESHOLD_PERCENTAGE); - if(localLOGV) Log.v(TAG, "Threshold Percentage="+value); - //evaluate threshold value - return mTotalMemory*value; - } - - /** - * Constructor to run service. initializes the disk space threshold value - * and posts an empty message to kickstart the process. - */ - public DeviceMemoryMonitorService(Context context) { - mLastReportedFreeMemTime = 0; - mContext = context; - mContentResolver = mContext.getContentResolver(); - //create StatFs object - mFileStats = new StatFs(DATA_PATH); - //initialize block size - mBlkSize = mFileStats.getBlockSize(); - //initialize total storage on device - mTotalMemory = (mFileStats.getBlockCount()*mBlkSize)/100; - mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW); - mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK); - checkMemory(); - } - - - /** - * This method sends a notification to NotificationManager to display - * an error dialog indicating low disk space and launch the Installer - * application - */ - private final void sendNotification() { - if(localLOGV) Log.i(TAG, "Sending low memory notification"); - //log the event to event log with the amount of free storage(in bytes) left on the device - EventLog.writeEvent(EVENT_LOG_LOW_STORAGE_NOTIFICATION, mFreeMem); - // Pack up the values and broadcast them to everyone - Intent lowMemIntent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE); - lowMemIntent.putExtra("memory", mFreeMem); - lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - NotificationManager mNotificationMgr = - (NotificationManager)mContext.getSystemService( - Context.NOTIFICATION_SERVICE); - CharSequence title = mContext.getText( - com.android.internal.R.string.low_internal_storage_view_title); - CharSequence details = mContext.getText( - com.android.internal.R.string.low_internal_storage_view_text); - PendingIntent intent = PendingIntent.getActivity(mContext, 0, lowMemIntent, 0); - Notification notification = new Notification(); - notification.icon = com.android.internal.R.drawable.stat_notify_disk_full; - notification.tickerText = title; - notification.flags |= Notification.FLAG_NO_CLEAR; - notification.setLatestEventInfo(mContext, title, details, intent); - mNotificationMgr.notify(LOW_MEMORY_NOTIFICATION_ID, notification); - mContext.sendStickyBroadcast(mStorageLowIntent); - } - - /** - * Cancels low storage notification and sends OK intent. - */ - private final void cancelNotification() { - if(localLOGV) Log.i(TAG, "Canceling low memory notification"); - NotificationManager mNotificationMgr = - (NotificationManager)mContext.getSystemService( - Context.NOTIFICATION_SERVICE); - //cancel notification since memory has been freed - mNotificationMgr.cancel(LOW_MEMORY_NOTIFICATION_ID); - - mContext.removeStickyBroadcast(mStorageLowIntent); - mContext.sendBroadcast(mStorageOkIntent); - } - - public void updateMemory() { - ActivityManagerService ams = (ActivityManagerService)ServiceManager.getService("activity"); - int callingUid = getCallingUid(); - if(callingUid != Process.SYSTEM_UID) { - return; - } - //remove queued messages - mHandler.removeMessages(DEVICE_MEMORY_WHAT); - //force an early check - checkMemory(); - } -} diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java index 04b1900..44f70f0 100644 --- a/services/java/com/android/server/DeviceStorageMonitorService.java +++ b/services/java/com/android/server/DeviceStorageMonitorService.java @@ -149,7 +149,7 @@ class DeviceStorageMonitorService extends Binder { mClearingCache = true; try { IPackageManager.Stub.asInterface(ServiceManager.getService("package")). - freeApplicationCache(getMemThreshold(), observer); + freeStorageAndNotify(getMemThreshold(), observer); } catch (RemoteException e) { Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e); mClearingCache = false; diff --git a/services/java/com/android/server/GadgetService.java b/services/java/com/android/server/GadgetService.java index 4e49253..4be9ed5 100644 --- a/services/java/com/android/server/GadgetService.java +++ b/services/java/com/android/server/GadgetService.java @@ -19,30 +19,264 @@ package com.android.server; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.PackageItemInfo; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; import android.gadget.GadgetManager; import android.gadget.GadgetInfo; +import android.os.Binder; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; import com.android.internal.gadget.IGadgetService; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + class GadgetService extends IGadgetService.Stub { private static final String TAG = "GadgetService"; + static class GadgetId { + int gadgetId; + String hostPackage; + GadgetInfo info; + } + Context mContext; + PackageManager mPackageManager; + ArrayList<GadgetInfo> mInstalledProviders; + int mNextGadgetId = 1; + ArrayList<GadgetId> mGadgetIds = new ArrayList(); GadgetService(Context context) { mContext = context; + mPackageManager = context.getPackageManager(); + mInstalledProviders = getGadgetList(); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + synchronized (mGadgetIds) { + int N = mInstalledProviders.size(); + pw.println("Providers: (size=" + N + ")"); + for (int i=0; i<N; i++) { + GadgetInfo info = mInstalledProviders.get(i); + pw.println(" [" + i + "] provder=" + info.provider + + " min=(" + info.minWidth + "x" + info.minHeight + ")" + + " updatePeriodMillis=" + info.updatePeriodMillis + + " initialLayout=" + info.initialLayout); + } + + N = mGadgetIds.size(); + pw.println("GadgetIds: (size=" + N + ")"); + for (int i=0; i<N; i++) { + GadgetId id = mGadgetIds.get(i); + pw.println(" [" + i + "] gadgetId=" + id.gadgetId + " host=" + id.hostPackage + + " provider=" + (id.info == null ? "null" : id.info.provider)); + } + } } public int allocateGadgetId(String hostPackage) { - return 42; + synchronized (mGadgetIds) { + // TODO: Check for pick permission + int gadgetId = mNextGadgetId++; + + GadgetId id = new GadgetId(); + id.gadgetId = gadgetId; + id.hostPackage = hostPackage; + + mGadgetIds.add(id); + + return gadgetId; + } } public void deleteGadgetId(int gadgetId) { + synchronized (mGadgetIds) { + String callingPackage = getCallingPackage(); + final int N = mGadgetIds.size(); + for (int i=0; i<N; i++) { + GadgetId id = mGadgetIds.get(i); + if (canAccessGadgetId(id, callingPackage)) { + mGadgetIds.remove(i); + // TODO: Notify someone? + return; + } + } + } } public void bindGadgetId(int gadgetId, ComponentName provider) { - sendEnabled(provider); + synchronized (mGadgetIds) { + GadgetId id = lookupGadgetIdLocked(gadgetId); + if (id == null) { + throw new IllegalArgumentException("bad gadgetId"); // TODO: use a better exception + } + if (id.info != null) { + throw new IllegalArgumentException("gadgetId " + gadgetId + " already bound to " + + id.info.provider); + } + GadgetInfo info = lookupGadgetInfoLocked(provider); + if (info == null) { + throw new IllegalArgumentException("not a gadget provider: " + provider); + } + + id.info = info; + } + } + + public GadgetInfo getGadgetInfo(int gadgetId) { + synchronized (mGadgetIds) { + GadgetId id = lookupGadgetIdLocked(gadgetId); + if (id != null) { + return id.info; + } + return null; + } + } + + public List<GadgetInfo> getInstalledProviders() { + synchronized (mGadgetIds) { + return new ArrayList<GadgetInfo>(mInstalledProviders); + } + } + + boolean canAccessGadgetId(GadgetId id, String callingPackage) { + if (id.hostPackage.equals(callingPackage)) { + return true; + } + if (id.info != null && id.info.provider.getPackageName().equals(callingPackage)) { + return true; + } + // TODO: Check for the pick permission + //if (has permission) { + // return true; + //} + //return false; + return true; + } + + private GadgetId lookupGadgetIdLocked(int gadgetId) { + String callingPackage = getCallingPackage(); + final int N = mGadgetIds.size(); + for (int i=0; i<N; i++) { + GadgetId id = mGadgetIds.get(i); + if (canAccessGadgetId(id, callingPackage)) { + return id; + } + } + return null; + } + + GadgetInfo lookupGadgetInfoLocked(ComponentName provider) { + final int N = mInstalledProviders.size(); + for (int i=0; i<N; i++) { + GadgetInfo info = mInstalledProviders.get(i); + if (info.provider.equals(provider)) { + return info; + } + } + return null; + } + + ArrayList<GadgetInfo> getGadgetList() { + PackageManager pm = mPackageManager; + + // TODO: We have these as different actions. I wonder if it makes more sense to + // have like a GADGET_ACTION, and then subcommands. It's kind of arbitrary that + // we look for GADGET_UPDATE_ACTION and not any of the other gadget actions. + Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent, + PackageManager.GET_META_DATA); + + ArrayList<GadgetInfo> result = new ArrayList<GadgetInfo>(); + + final int N = broadcastReceivers.size(); + for (int i=0; i<N; i++) { + ResolveInfo ri = broadcastReceivers.get(i); + ActivityInfo ai = ri.activityInfo; + GadgetInfo gi = parseGadgetInfoXml(new ComponentName(ai.packageName, ai.name), + ri.activityInfo); + if (gi != null) { + result.add(gi); + } + } + + return result; + } + + private GadgetInfo parseGadgetInfoXml(ComponentName component, + PackageItemInfo packageItemInfo) { + GadgetInfo gi = null; + + XmlResourceParser parser = null; + try { + parser = packageItemInfo.loadXmlMetaData(mPackageManager, + GadgetManager.GADGET_PROVIDER_META_DATA); + if (parser == null) { + Log.w(TAG, "No " + GadgetManager.GADGET_PROVIDER_META_DATA + " meta-data for " + + "gadget provider '" + component + '\''); + return null; + } + + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + // drain whitespace, comments, etc. + } + + String nodeName = parser.getName(); + if (!"gadget-provider".equals(nodeName)) { + Log.w(TAG, "Meta-data does not start with gadget-provider tag for" + + " gadget provider '" + component + '\''); + return null; + } + + gi = new GadgetInfo(); + + gi.provider = component; + + TypedArray sa = mContext.getResources().obtainAttributes(attrs, + com.android.internal.R.styleable.GadgetProviderInfo); + gi.minWidth = sa.getDimensionPixelSize( + com.android.internal.R.styleable.GadgetProviderInfo_minWidth, 0); + gi.minHeight = sa.getDimensionPixelSize( + com.android.internal.R.styleable.GadgetProviderInfo_minHeight, 0); + gi.updatePeriodMillis = sa.getInt( + com.android.internal.R.styleable.GadgetProviderInfo_updatePeriodMillis, 0); + gi.initialLayout = sa.getResourceId( + com.android.internal.R.styleable.GadgetProviderInfo_initialLayout, 0); + sa.recycle(); + } catch (Exception e) { + // Ok to catch Exception here, because anything going wrong because + // of what a client process passes to us should not be fatal for the + // system process. + Log.w(TAG, "XML parsing failed for gadget provider '" + component + '\'', e); + } finally { + if (parser != null) parser.close(); + } + return gi; } void sendEnabled(ComponentName provider) { @@ -51,14 +285,8 @@ class GadgetService extends IGadgetService.Stub mContext.sendBroadcast(intent); } - public GadgetInfo getGadgetInfo(int gadgetId) { - GadgetInfo info = new GadgetInfo(); - info.provider = new ComponentName("com.android.gadgethost", - "com.android.gadgethost.TestGadgetProvider"); - info.minWidth = 0; - info.minHeight = 0; - info.updatePeriodMillis = 60 * 1000; // 60s - return info; + String getCallingPackage() { + return mPackageManager.getNameForUid(getCallingUid()); } } diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index f0d5eaf..58ad426 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -26,6 +26,8 @@ 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; @@ -892,32 +894,57 @@ class PackageManagerService extends IPackageManager.Stub { } - public void freeApplicationCache(final long freeStorageSize, final IPackageDataObserver observer) { + public void freeStorageAndNotify(final long freeStorageSize, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); // Queue up an async operation since clearing cache may take a little while. mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); - boolean succeded = true; + int retCode = -1; if (mInstaller != null) { - int retCode = mInstaller.freeCache(freeStorageSize); + retCode = mInstaller.freeCache(freeStorageSize); if (retCode < 0) { Log.w(TAG, "Couldn't clear application caches"); - succeded = false; - } + } } //end if mInstaller - if(observer != null) { + if (observer != null) { try { - observer.onRemoveCompleted(null, succeded); + observer.onRemoveCompleted(null, (retCode >= 0)); } catch (RemoteException e) { - Log.i(TAG, "Observer no longer exists."); + Log.w(TAG, "RemoveException when invoking call back"); } - } //end if observer - } //end run + } + } }); } + public void freeStorage(final long freeStorageSize, final PendingIntent opFinishedIntent) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CLEAR_APP_CACHE, null); + // Queue up an async operation since clearing cache may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + int retCode = -1; + if (mInstaller != null) { + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Log.w(TAG, "Couldn't clear application caches"); + } + } + if(opFinishedIntent != null) { + try { + // Callback via pending intent + opFinishedIntent.send((retCode >= 0) ? 1 : 0); + } catch (CanceledException e1) { + Log.i(TAG, "Failed to send pending intent"); + } + } + } + }); + } + public ActivityInfo getActivityInfo(ComponentName component, int flags) { synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 881add1..7ed0aec 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -165,6 +165,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * value to another, when no window animation is driving it. */ static final int DEFAULT_DIM_DURATION = 200; + + /** Adjustment to time to perform a dim, to make it more dramatic. + */ + static final int DIM_DURATION_MULTIPLIER = 6; static final int UPDATE_FOCUS_NORMAL = 0; static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; @@ -1129,7 +1133,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.mSurface != null && !mDisplayFrozen) { // If we are not currently running the exit animation, we // need to see about starting one. - if (win.isVisible()) { + if (win.isVisibleLw()) { int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; @@ -1339,7 +1343,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo win.mViewVisibility = viewVisibility; if (viewVisibility == View.VISIBLE && (win.mAppToken == null || !win.mAppToken.clientHidden)) { - displayed = !win.isVisible(); + displayed = !win.isVisibleLw(); if (win.mExiting) { win.mExiting = false; win.mAnimation = null; @@ -1389,7 +1393,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } - if (win.isVisible() && + if (win.isVisibleLw() && applyAnimationLocked(win, transit, false)) { win.mExiting = true; mKeyWaiter.finishedKey(session, client, true, @@ -2215,6 +2219,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (swin != null && (swin.mDrawPending || swin.mCommitDrawPending)) { swin.mPolicyVisibility = false; + swin.mPolicyVisibilityAfterAnim = false; } } @@ -2901,7 +2906,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final int N = mWindows.size(); for (int i=0; i<N; i++) { WindowState w = (WindowState)mWindows.get(i); - if (w.isVisible() && !w.isDisplayedLw()) { + if (w.isVisibleLw() && !w.isDisplayedLw()) { return; } } @@ -4126,7 +4131,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo topErrWindow = child; } } - if (!child.isVisible()) { + if (!child.isVisibleLw()) { //Log.i(TAG, "Not visible!"); continue; } @@ -4455,7 +4460,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } if (mLastWin == null || !mLastWin.mToken.paused - || !mLastWin.isVisible()) { + || !mLastWin.isVisibleLw()) { // If the current window has been paused, we aren't -really- // finished... so let the waiters still wait. mLastWin = null; @@ -4990,6 +4995,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final boolean mIsImWindow; int mViewVisibility; boolean mPolicyVisibility = true; + boolean mPolicyVisibilityAfterAnim = true; boolean mAppFreezing; Surface mSurface; boolean mAttachedHidden; // is our parent window hidden? @@ -5634,6 +5640,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mAnimation = null; mAnimLayer = mLayer; mHasTransformation = false; + mPolicyVisibility = mPolicyVisibilityAfterAnim; mTransformation.clear(); if (mHasDrawn && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING @@ -5782,7 +5789,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * surface, or we are in the process of running an exit animation * that will remove the surface. */ - boolean isVisible() { + public boolean isVisibleLw() { final AppWindowToken atoken = mAppToken; return mSurface != null && mPolicyVisibility && !mAttachedHidden && (atoken == null || !atoken.hiddenRequested) @@ -5937,18 +5944,32 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return mHasDrawn; } - public void showLw() { - if (!mPolicyVisibility) { + public void showLw(boolean doAnimation) { + if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim) { mSurfacesChanged = true; mPolicyVisibility = true; + mPolicyVisibilityAfterAnim = true; + if (doAnimation) { + applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true); + } requestAnimationLocked(0); } } - public void hideLw() { - if (mPolicyVisibility) { + public void hideLw(boolean doAnimation) { + if (mPolicyVisibility || mPolicyVisibilityAfterAnim) { mSurfacesChanged = true; - mPolicyVisibility = false; + if (doAnimation) { + applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false); + if (mAnimation == null) { + doAnimation = false; + } + } + if (doAnimation) { + mPolicyVisibilityAfterAnim = false; + } else { + mPolicyVisibility = false; + } requestAnimationLocked(0); } } @@ -5974,7 +5995,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pw.println(prefix + "mTargetAppToken=" + mTargetAppToken); pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility) + " mPolicyVisibility=" + mPolicyVisibility - + " mAttachedHidden=" + mAttachedHidden + + " (after=" + mPolicyVisibilityAfterAnim + + ") mAttachedHidden=" + mAttachedHidden + " mLastHidden=" + mLastHidden + " mHaveFrame=" + mHaveFrame); pw.println(prefix + "Requested w=" + mRequestedWidth + " h=" + mRequestedHeight @@ -7590,6 +7612,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo long duration = (w.mAnimating && w.mAnimation != null) ? w.mAnimation.computeDurationHint() : DEFAULT_DIM_DURATION; + if (target > mDimTargetAlpha) { + // This is happening behind the activity UI, + // so we can make it run a little longer to + // give a stronger impression without disrupting + // the user. + duration *= DIM_DURATION_MULTIPLIER; + } mDimTargetAlpha = target; mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) / duration; @@ -7660,6 +7689,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (more) { if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface + ": alpha=" + mDimCurrentAlpha); + mLastDimAnimTime = currentTime; mDimSurface.setAlpha(mDimCurrentAlpha); animating = true; } else { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index d0c2dac..361c7f7 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -118,6 +118,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final boolean DEBUG_SERVICE = localLOGV || false; static final boolean DEBUG_VISBILITY = localLOGV || false; static final boolean DEBUG_PROCESSES = localLOGV || false; + static final boolean DEBUG_USER_LEAVING = localLOGV || false; static final boolean VALIDATE_TOKENS = false; static final boolean SHOW_ACTIVITY_START_TIME = true; @@ -327,6 +328,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean mBroadcastsScheduled = false; /** + * Set to indicate whether to issue an onUserLeaving callback when a + * newly launched activity is being brought in front of us. + */ + boolean mUserLeaving = false; + + /** * When we are in the process of pausing an activity, before starting the * next one, this variable holds the activity that is currently being paused. */ @@ -1768,7 +1775,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void startPausingLocked() { + private final void startPausingLocked(boolean userLeaving) { if (mPausingActivity != null) { RuntimeException e = new RuntimeException(); Log.e(TAG, "Trying to pause when pause is already pending for " @@ -1795,7 +1802,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen EventLog.writeEvent(LOG_AM_PAUSE_ACTIVITY, System.identityHashCode(prev), prev.shortComponentName); - prev.app.thread.schedulePauseActivity(prev, prev.finishing, + prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving, prev.configChangeFlags); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. @@ -2092,6 +2099,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Find the first activity that is not finishing. HistoryRecord next = topRunningActivityLocked(null); + // Remember how we'll process this pause/resume situation, and ensure + // that the state is reset however we wind up proceeding. + final boolean userLeaving = mUserLeaving; + mUserLeaving = false; + if (next == null) { // There are no more activities! Let's just start up the // Launcher... @@ -2154,7 +2166,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // can be resumed... if (mResumedActivity != null) { if (DEBUG_SWITCH) Log.v(TAG, "Skip resume: need to start pausing"); - startPausingLocked(); + startPausingLocked(userLeaving); return true; } @@ -2380,12 +2392,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + // Place a new activity at top of stack, so it is next to interact + // with the user. if (addPos < 0) { addPos = mHistory.size(); } - // Place activity at top of stack, so it is next to interact - // with the user. + // If we are not placing the new activity frontmost, we do not want + // to deliver the onUserLeaving callback to the actual frontmost + // activity + if (addPos < NH) { + mUserLeaving = false; + if (DEBUG_USER_LEAVING) Log.v(TAG, "startActivity() behind front, mUserLeaving=false"); + } + + // Slot the activity into the history stack and proceed mHistory.add(addPos, r); r.inHistory = true; r.frontOfTask = newTask; @@ -2673,6 +2694,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; + // 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 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 @@ -3360,7 +3387,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mPausingActivity == null) { if (DEBUG_PAUSE) Log.v(TAG, "Finish needs to pause: " + r); - startPausingLocked(); + if (DEBUG_USER_LEAVING) Log.v(TAG, "finish() => pause with userLeaving=false"); + startPausingLocked(false); } } else if (r.state != ActivityState.PAUSING) { @@ -6875,7 +6903,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // run and release the wake lock when all done. if (mPausingActivity == null) { if (DEBUG_PAUSE) Log.v(TAG, "Sleep needs to pause..."); - startPausingLocked(); + if (DEBUG_USER_LEAVING) Log.v(TAG, "Sleep => pause with userLeaving=false"); + startPausingLocked(false); } } } diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java index 2d2faeb..3b6e354 100644 --- a/services/java/com/android/server/status/StatusBarService.java +++ b/services/java/com/android/server/status/StatusBarService.java @@ -331,6 +331,7 @@ public class StatusBarService extends IStatusBar.Stub mPixelFormat); lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.setTitle("StatusBar"); + lp.windowAnimations = R.style.Animation_StatusBar; WindowManagerImpl.getDefault().addView(mStatusBarView, lp); } |