summaryrefslogtreecommitdiffstats
path: root/services/java/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android')
-rw-r--r--services/java/com/android/server/DeviceMemoryMonitor.java288
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java2
-rw-r--r--services/java/com/android/server/GadgetService.java248
-rw-r--r--services/java/com/android/server/PackageManagerService.java47
-rw-r--r--services/java/com/android/server/WindowManagerService.java56
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java43
-rw-r--r--services/java/com/android/server/status/StatusBarService.java1
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);
}