summaryrefslogtreecommitdiffstats
path: root/services/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com')
-rw-r--r--services/java/com/android/server/AppWidgetService.java6
-rw-r--r--services/java/com/android/server/AssetAtlasService.java730
-rw-r--r--services/java/com/android/server/BackupManagerService.java11
-rw-r--r--services/java/com/android/server/BatteryService.java9
-rw-r--r--services/java/com/android/server/BluetoothManagerService.java6
-rw-r--r--services/java/com/android/server/CountryDetectorService.java9
-rw-r--r--services/java/com/android/server/FgThread.java65
-rw-r--r--services/java/com/android/server/IdleMaintenanceService.java186
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java23
-rw-r--r--services/java/com/android/server/IoThread.java62
-rw-r--r--services/java/com/android/server/LocationManagerService.java9
-rw-r--r--services/java/com/android/server/MountService.java12
-rw-r--r--services/java/com/android/server/NativeDaemonConnector.java5
-rw-r--r--services/java/com/android/server/NetworkTimeUpdateService.java7
-rw-r--r--services/java/com/android/server/SystemServer.java49
-rw-r--r--services/java/com/android/server/UiThread.java71
-rw-r--r--services/java/com/android/server/Watchdog.java207
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java78
-rw-r--r--services/java/com/android/server/accounts/AccountManagerService.java7
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java51
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java81
-rw-r--r--services/java/com/android/server/am/ActivityStack.java208
-rw-r--r--services/java/com/android/server/am/ActivityStackSupervisor.java76
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java14
-rw-r--r--services/java/com/android/server/am/TaskRecord.java18
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java9
-rw-r--r--services/java/com/android/server/content/SyncManager.java9
-rw-r--r--services/java/com/android/server/display/DisplayManagerService.java5
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java7
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java7
-rw-r--r--services/java/com/android/server/pm/Installer.java6
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java115
-rw-r--r--services/java/com/android/server/power/PowerManagerService.java23
-rw-r--r--services/java/com/android/server/power/ShutdownThread.java7
-rw-r--r--services/java/com/android/server/usb/UsbDebuggingManager.java10
-rw-r--r--services/java/com/android/server/usb/UsbDeviceManager.java9
-rw-r--r--services/java/com/android/server/wm/DimLayer.java27
-rw-r--r--services/java/com/android/server/wm/DisplayContent.java45
-rw-r--r--services/java/com/android/server/wm/StackBox.java94
-rw-r--r--services/java/com/android/server/wm/TaskStack.java159
-rw-r--r--services/java/com/android/server/wm/WindowAnimator.java177
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java111
-rw-r--r--services/java/com/android/server/wm/WindowState.java14
-rw-r--r--services/java/com/android/server/wm/WindowStateAnimator.java2
44 files changed, 2074 insertions, 762 deletions
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index d5715a5..5b76f39 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -27,7 +27,6 @@ import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -37,6 +36,7 @@ import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
@@ -63,9 +63,7 @@ class AppWidgetService extends IAppWidgetService.Stub
AppWidgetService(Context context) {
mContext = context;
- HandlerThread handlerThread = new HandlerThread("AppWidgetService -- Save state");
- handlerThread.start();
- mSaveStateHandler = new Handler(handlerThread.getLooper());
+ mSaveStateHandler = BackgroundThread.getHandler();
mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0, mSaveStateHandler);
diff --git a/services/java/com/android/server/AssetAtlasService.java b/services/java/com/android/server/AssetAtlasService.java
new file mode 100644
index 0000000..b18be1c
--- /dev/null
+++ b/services/java/com/android/server/AssetAtlasService.java
@@ -0,0 +1,730 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Atlas;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.Drawable;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.view.GraphicBuffer;
+import android.view.IAssetAtlas;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This service is responsible for packing preloaded bitmaps into a single
+ * atlas texture. The resulting texture can be shared across processes to
+ * reduce overall memory usage.
+ *
+ * @hide
+ */
+public class AssetAtlasService extends IAssetAtlas.Stub {
+ /**
+ * Name of the <code>AssetAtlasService</code>.
+ */
+ public static final String ASSET_ATLAS_SERVICE = "assetatlas";
+
+ private static final String LOG_TAG = "Atlas";
+
+ // Turns debug logs on/off. Debug logs are kept to a minimum and should
+ // remain on to diagnose issues
+ private static final boolean DEBUG_ATLAS = true;
+
+ // When set to true the content of the atlas will be saved to disk
+ // in /data/system/atlas.png. The shared GraphicBuffer may be empty
+ private static final boolean DEBUG_ATLAS_TEXTURE = false;
+
+ // Minimum size in pixels to consider for the resulting texture
+ private static final int MIN_SIZE = 768;
+ // Maximum size in pixels to consider for the resulting texture
+ private static final int MAX_SIZE = 2048;
+ // Increment in number of pixels between size variants when looking
+ // for the best texture dimensions
+ private static final int STEP = 64;
+
+ // This percentage of the total number of pixels represents the minimum
+ // number of pixels we want to be able to pack in the atlas
+ private static final float PACKING_THRESHOLD = 0.8f;
+
+ // Defines the number of int fields used to represent a single entry
+ // in the atlas map. This number defines the size of the array returned
+ // by the getMap(). See the mAtlasMap field for more information
+ private static final int ATLAS_MAP_ENTRY_FIELD_COUNT = 4;
+
+ // Specifies how our GraphicBuffer will be used. To get proper swizzling
+ // the buffer will be written to using OpenGL (from JNI) so we can leave
+ // the software flag set to "never"
+ private static final int GRAPHIC_BUFFER_USAGE = GraphicBuffer.USAGE_SW_READ_NEVER |
+ GraphicBuffer.USAGE_SW_WRITE_NEVER | GraphicBuffer.USAGE_HW_TEXTURE;
+
+ // This boolean is set to true if an atlas was successfully
+ // computed and rendered
+ private final AtomicBoolean mAtlasReady = new AtomicBoolean(false);
+
+ private final Context mContext;
+
+ // Version name of the current build, used to identify changes to assets list
+ private final String mVersionName;
+
+ // Holds the atlas' data. This buffer can be mapped to
+ // OpenGL using an EGLImage
+ private GraphicBuffer mBuffer;
+
+ // Describes how bitmaps are placed in the atlas. Each bitmap is
+ // represented by several entries in the array:
+ // int0: SkBitmap*, the native bitmap object
+ // int1: x position
+ // int2: y position
+ // int3: rotated, 1 if the bitmap must be rotated, 0 otherwise
+ // NOTE: This will need to be handled differently to support 64 bit pointers
+ private int[] mAtlasMap;
+
+ /**
+ * Creates a new service. Upon creating, the service will gather the list of
+ * assets to consider for packing into the atlas and spawn a new thread to
+ * start the packing work.
+ *
+ * @param context The context giving access to preloaded resources
+ */
+ public AssetAtlasService(Context context) {
+ mContext = context;
+ mVersionName = queryVersionName(context);
+
+ ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>(300);
+ int totalPixelCount = 0;
+
+ // We only care about drawables that hold bitmaps
+ final Resources resources = context.getResources();
+ final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();
+
+ final int count = drawables.size();
+ for (int i = 0; i < count; i++) {
+ final Bitmap bitmap = drawables.valueAt(i).getBitmap();
+ if (bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888) {
+ bitmaps.add(bitmap);
+ totalPixelCount += bitmap.getWidth() * bitmap.getHeight();
+ }
+ }
+
+ // Our algorithms perform better when the bitmaps are first sorted
+ // The comparator will sort the bitmap by width first, then by height
+ Collections.sort(bitmaps, new Comparator<Bitmap>() {
+ @Override
+ public int compare(Bitmap b1, Bitmap b2) {
+ if (b1.getWidth() == b2.getWidth()) {
+ return b2.getHeight() - b1.getHeight();
+ }
+ return b2.getWidth() - b1.getWidth();
+ }
+ });
+
+ // Kick off the packing work on a worker thread
+ new Thread(new Renderer(bitmaps, totalPixelCount)).start();
+ }
+
+ /**
+ * Queries the version name stored in framework's AndroidManifest.
+ * The version name can be used to identify possible changes to
+ * framework resources.
+ *
+ * @see #getBuildIdentifier(String)
+ */
+ private static String queryVersionName(Context context) {
+ try {
+ String packageName = context.getPackageName();
+ PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
+ return info.versionName;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(LOG_TAG, "Could not get package info", e);
+ }
+ return null;
+ }
+
+ /**
+ * Callback invoked by the server thread to indicate we can now run
+ * 3rd party code.
+ */
+ public void systemReady() {
+ }
+
+ /**
+ * The renderer does all the work:
+ */
+ private class Renderer implements Runnable {
+ private final ArrayList<Bitmap> mBitmaps;
+ private final int mPixelCount;
+
+ private int mNativeBitmap;
+
+ // Used for debugging only
+ private Bitmap mAtlasBitmap;
+
+ Renderer(ArrayList<Bitmap> bitmaps, int pixelCount) {
+ mBitmaps = bitmaps;
+ mPixelCount = pixelCount;
+ }
+
+ /**
+ * 1. On first boot or after every update, brute-force through all the
+ * possible atlas configurations and look for the best one (maximimize
+ * number of packed assets and minimize texture size)
+ * a. If a best configuration was computed, write it out to disk for
+ * future use
+ * 2. Read best configuration from disk
+ * 3. Compute the packing using the best configuration
+ * 4. Allocate a GraphicBuffer
+ * 5. Render assets in the buffer
+ */
+ @Override
+ public void run() {
+ Configuration config = chooseConfiguration(mBitmaps, mPixelCount, mVersionName);
+ if (DEBUG_ATLAS) Log.d(LOG_TAG, "Loaded configuration: " + config);
+
+ if (config != null) {
+ mBuffer = GraphicBuffer.create(config.width, config.height,
+ PixelFormat.RGBA_8888, GRAPHIC_BUFFER_USAGE);
+
+ if (mBuffer != null) {
+ Atlas atlas = new Atlas(config.type, config.width, config.height, config.flags);
+ if (renderAtlas(mBuffer, atlas, config.count)) {
+ mAtlasReady.set(true);
+ }
+ }
+ }
+ }
+
+ /**
+ * Renders a list of bitmaps into the atlas. The position of each bitmap
+ * was decided by the packing algorithm and will be honored by this
+ * method. If need be this method will also rotate bitmaps.
+ *
+ * @param buffer The buffer to render the atlas entries into
+ * @param atlas The atlas to pack the bitmaps into
+ * @param packCount The number of bitmaps that will be packed in the atlas
+ *
+ * @return true if the atlas was rendered, false otherwise
+ */
+ @SuppressWarnings("MismatchedReadAndWriteOfArray")
+ private boolean renderAtlas(GraphicBuffer buffer, Atlas atlas, int packCount) {
+ // Use a Source blend mode to improve performance, the target bitmap
+ // will be zero'd out so there's no need to waste time applying blending
+ final Paint paint = new Paint();
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+
+ // We always render the atlas into a bitmap. This bitmap is then
+ // uploaded into the GraphicBuffer using OpenGL to swizzle the content
+ final Canvas canvas = acquireCanvas(buffer.getWidth(), buffer.getHeight());
+ if (canvas == null) return false;
+
+ final Atlas.Entry entry = new Atlas.Entry();
+
+ mAtlasMap = new int[packCount * ATLAS_MAP_ENTRY_FIELD_COUNT];
+ int[] atlasMap = mAtlasMap;
+ int mapIndex = 0;
+
+ boolean result = false;
+ try {
+ final long startRender = System.nanoTime();
+ final int count = mBitmaps.size();
+
+ for (int i = 0; i < count; i++) {
+ final Bitmap bitmap = mBitmaps.get(i);
+ if (atlas.pack(bitmap.getWidth(), bitmap.getHeight(), entry) != null) {
+ // We have more bitmaps to pack than the current configuration
+ // says, we were most likely not able to detect a change in the
+ // list of preloaded drawables, abort and delete the configuration
+ if (mapIndex >= mAtlasMap.length) {
+ deleteDataFile();
+ break;
+ }
+
+ canvas.save();
+ canvas.translate(entry.x, entry.y);
+ if (entry.rotated) {
+ canvas.translate(bitmap.getHeight(), 0.0f);
+ canvas.rotate(90.0f);
+ }
+ canvas.drawBitmap(bitmap, 0.0f, 0.0f, null);
+ canvas.restore();
+
+ atlasMap[mapIndex++] = bitmap.mNativeBitmap;
+ atlasMap[mapIndex++] = entry.x;
+ atlasMap[mapIndex++] = entry.y;
+ atlasMap[mapIndex++] = entry.rotated ? 1 : 0;
+ }
+ }
+
+ final long endRender = System.nanoTime();
+ if (mNativeBitmap != 0) {
+ result = nUploadAtlas(buffer, mNativeBitmap);
+ }
+
+ final long endUpload = System.nanoTime();
+ if (DEBUG_ATLAS) {
+ float renderDuration = (endRender - startRender) / 1000.0f / 1000.0f;
+ float uploadDuration = (endUpload - endRender) / 1000.0f / 1000.0f;
+ Log.d(LOG_TAG, String.format("Rendered atlas in %.2fms (%.2f+%.2fms)",
+ renderDuration + uploadDuration, renderDuration, uploadDuration));
+ }
+
+ } finally {
+ releaseCanvas(canvas);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a Canvas for the specified buffer. If {@link #DEBUG_ATLAS_TEXTURE}
+ * is turned on, the returned Canvas will render into a local bitmap that
+ * will then be saved out to disk for debugging purposes.
+ * @param width
+ * @param height
+ */
+ private Canvas acquireCanvas(int width, int height) {
+ if (DEBUG_ATLAS_TEXTURE) {
+ mAtlasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ return new Canvas(mAtlasBitmap);
+ } else {
+ Canvas canvas = new Canvas();
+ mNativeBitmap = nAcquireAtlasCanvas(canvas, width, height);
+ return canvas;
+ }
+ }
+
+ /**
+ * Releases the canvas used to render into the buffer. Calling this method
+ * will release any resource previously acquired. If {@link #DEBUG_ATLAS_TEXTURE}
+ * is turend on, calling this method will write the content of the atlas
+ * to disk in /data/system/atlas.png for debugging.
+ */
+ private void releaseCanvas(Canvas canvas) {
+ if (DEBUG_ATLAS_TEXTURE) {
+ canvas.setBitmap(null);
+
+ File systemDirectory = new File(Environment.getDataDirectory(), "system");
+ File dataFile = new File(systemDirectory, "atlas.png");
+
+ try {
+ FileOutputStream out = new FileOutputStream(dataFile);
+ mAtlasBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ out.close();
+ } catch (FileNotFoundException e) {
+ // Ignore
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ mAtlasBitmap.recycle();
+ mAtlasBitmap = null;
+ } else {
+ nReleaseAtlasCanvas(canvas, mNativeBitmap);
+ }
+ }
+ }
+
+ private static native int nAcquireAtlasCanvas(Canvas canvas, int width, int height);
+ private static native void nReleaseAtlasCanvas(Canvas canvas, int bitmap);
+ private static native boolean nUploadAtlas(GraphicBuffer buffer, int bitmap);
+
+ @Override
+ public GraphicBuffer getBuffer() throws RemoteException {
+ return mAtlasReady.get() ? mBuffer : null;
+ }
+
+ @Override
+ public int[] getMap() throws RemoteException {
+ return mAtlasReady.get() ? mAtlasMap : null;
+ }
+
+ /**
+ * Finds the best atlas configuration to pack the list of supplied bitmaps.
+ * This method takes advantage of multi-core systems by spawning a number
+ * of threads equal to the number of available cores.
+ */
+ private static Configuration computeBestConfiguration(
+ ArrayList<Bitmap> bitmaps, int pixelCount) {
+ if (DEBUG_ATLAS) Log.d(LOG_TAG, "Computing best atlas configuration...");
+
+ long begin = System.nanoTime();
+ List<WorkerResult> results = Collections.synchronizedList(new ArrayList<WorkerResult>());
+
+ // Don't bother with an extra thread if there's only one processor
+ int cpuCount = Runtime.getRuntime().availableProcessors();
+ if (cpuCount == 1) {
+ new ComputeWorker(MIN_SIZE, MAX_SIZE, STEP, bitmaps, pixelCount, results, null).run();
+ } else {
+ int start = MIN_SIZE;
+ int end = MAX_SIZE - (cpuCount - 1) * STEP;
+ int step = STEP * cpuCount;
+
+ final CountDownLatch signal = new CountDownLatch(cpuCount);
+
+ for (int i = 0; i < cpuCount; i++, start += STEP, end += STEP) {
+ ComputeWorker worker = new ComputeWorker(start, end, step,
+ bitmaps, pixelCount, results, signal);
+ new Thread(worker, "Atlas Worker #" + (i + 1)).start();
+ }
+
+ try {
+ signal.await(10, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.w(LOG_TAG, "Could not complete configuration computation");
+ return null;
+ }
+ }
+
+ // Maximize the number of packed bitmaps, minimize the texture size
+ Collections.sort(results, new Comparator<WorkerResult>() {
+ @Override
+ public int compare(WorkerResult r1, WorkerResult r2) {
+ int delta = r2.count - r1.count;
+ if (delta != 0) return delta;
+ return r1.width * r1.height - r2.width * r2.height;
+ }
+ });
+
+ if (DEBUG_ATLAS) {
+ float delay = (System.nanoTime() - begin) / 1000.0f / 1000.0f / 1000.0f;
+ Log.d(LOG_TAG, String.format("Found best atlas configuration in %.2fs", delay));
+ }
+
+ WorkerResult result = results.get(0);
+ return new Configuration(result.type, result.width, result.height, result.count);
+ }
+
+ /**
+ * Returns the path to the file containing the best computed
+ * atlas configuration.
+ */
+ private static File getDataFile() {
+ File systemDirectory = new File(Environment.getDataDirectory(), "system");
+ return new File(systemDirectory, "framework_atlas.config");
+ }
+
+ private static void deleteDataFile() {
+ Log.w(LOG_TAG, "Current configuration inconsistent with assets list");
+ if (!getDataFile().delete()) {
+ Log.w(LOG_TAG, "Could not delete the current configuration");
+ }
+ }
+
+ private File getFrameworkResourcesFile() {
+ return new File(mContext.getApplicationInfo().sourceDir);
+ }
+
+ /**
+ * Returns the best known atlas configuration. This method will either
+ * read the configuration from disk or start a brute-force search
+ * and save the result out to disk.
+ */
+ private Configuration chooseConfiguration(ArrayList<Bitmap> bitmaps, int pixelCount,
+ String versionName) {
+ Configuration config = null;
+
+ final File dataFile = getDataFile();
+ if (dataFile.exists()) {
+ config = readConfiguration(dataFile, versionName);
+ }
+
+ if (config == null) {
+ config = computeBestConfiguration(bitmaps, pixelCount);
+ if (config != null) writeConfiguration(config, dataFile, versionName);
+ }
+
+ return config;
+ }
+
+ /**
+ * Writes the specified atlas configuration to the specified file.
+ */
+ private void writeConfiguration(Configuration config, File file, String versionName) {
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
+ writer.write(getBuildIdentifier(versionName));
+ writer.newLine();
+ writer.write(config.type.toString());
+ writer.newLine();
+ writer.write(String.valueOf(config.width));
+ writer.newLine();
+ writer.write(String.valueOf(config.height));
+ writer.newLine();
+ writer.write(String.valueOf(config.count));
+ writer.newLine();
+ writer.write(String.valueOf(config.flags));
+ writer.newLine();
+ } catch (FileNotFoundException e) {
+ Log.w(LOG_TAG, "Could not write " + file, e);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Could not write " + file, e);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Reads an atlas configuration from the specified file. This method
+ * returns null if an error occurs or if the configuration is invalid.
+ */
+ private Configuration readConfiguration(File file, String versionName) {
+ BufferedReader reader = null;
+ Configuration config = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
+
+ if (checkBuildIdentifier(reader, versionName)) {
+ Atlas.Type type = Atlas.Type.valueOf(reader.readLine());
+ int width = readInt(reader, MIN_SIZE, MAX_SIZE);
+ int height = readInt(reader, MIN_SIZE, MAX_SIZE);
+ int count = readInt(reader, 0, Integer.MAX_VALUE);
+ int flags = readInt(reader, Integer.MIN_VALUE, Integer.MAX_VALUE);
+
+ config = new Configuration(type, width, height, count, flags);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(LOG_TAG, "Invalid parameter value in " + file, e);
+ } catch (FileNotFoundException e) {
+ Log.w(LOG_TAG, "Could not read " + file, e);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Could not read " + file, e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ return config;
+ }
+
+ private static int readInt(BufferedReader reader, int min, int max) throws IOException {
+ return Math.max(min, Math.min(max, Integer.parseInt(reader.readLine())));
+ }
+
+ /**
+ * Compares the next line in the specified buffered reader to the current
+ * build identifier. Returns whether the two values are equal.
+ *
+ * @see #getBuildIdentifier(String)
+ */
+ private boolean checkBuildIdentifier(BufferedReader reader, String versionName)
+ throws IOException {
+ String deviceBuildId = getBuildIdentifier(versionName);
+ String buildId = reader.readLine();
+ return deviceBuildId.equals(buildId);
+ }
+
+ /**
+ * Returns an identifier for the current build that can be used to detect
+ * likely changes to framework resources. The build identifier is made of
+ * several distinct values:
+ *
+ * build fingerprint/framework version name/file size of framework resources apk
+ *
+ * Only the build fingerprint should be necessary on user builds but
+ * the other values are useful to detect changes on eng builds during
+ * development.
+ *
+ * This identifier does not attempt to be exact: a new identifier does not
+ * necessarily mean the preloaded drawables have changed. It is important
+ * however that whenever the list of preloaded drawables changes, this
+ * identifier changes as well.
+ *
+ * @see #checkBuildIdentifier(java.io.BufferedReader, String)
+ */
+ private String getBuildIdentifier(String versionName) {
+ return SystemProperties.get("ro.build.fingerprint", "") + '/' + versionName + '/' +
+ String.valueOf(getFrameworkResourcesFile().length());
+ }
+
+ /**
+ * Atlas configuration. Specifies the algorithm, dimensions and flags to use.
+ */
+ private static class Configuration {
+ final Atlas.Type type;
+ final int width;
+ final int height;
+ final int count;
+ final int flags;
+
+ Configuration(Atlas.Type type, int width, int height, int count) {
+ this(type, width, height, count, Atlas.FLAG_DEFAULTS);
+ }
+
+ Configuration(Atlas.Type type, int width, int height, int count, int flags) {
+ this.type = type;
+ this.width = width;
+ this.height = height;
+ this.count = count;
+ this.flags = flags;
+ }
+
+ @Override
+ public String toString() {
+ return type.toString() + " (" + width + "x" + height + ") flags=0x" +
+ Integer.toHexString(flags) + " count=" + count;
+ }
+ }
+
+ /**
+ * Used during the brute-force search to gather information about each
+ * variant of the packing algorithm.
+ */
+ private static class WorkerResult {
+ Atlas.Type type;
+ int width;
+ int height;
+ int count;
+
+ WorkerResult(Atlas.Type type, int width, int height, int count) {
+ this.type = type;
+ this.width = width;
+ this.height = height;
+ this.count = count;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s %dx%d", type.toString(), width, height);
+ }
+ }
+
+ /**
+ * A compute worker will try a finite number of variations of the packing
+ * algorithms and save the results in a supplied list.
+ */
+ private static class ComputeWorker implements Runnable {
+ private final int mStart;
+ private final int mEnd;
+ private final int mStep;
+ private final List<Bitmap> mBitmaps;
+ private final List<WorkerResult> mResults;
+ private final CountDownLatch mSignal;
+ private final int mThreshold;
+
+ /**
+ * Creates a new compute worker to brute-force through a range of
+ * packing algorithms variants.
+ *
+ * @param start The minimum texture width to try
+ * @param end The maximum texture width to try
+ * @param step The number of pixels to increment the texture width by at each step
+ * @param bitmaps The list of bitmaps to pack in the atlas
+ * @param pixelCount The total number of pixels occupied by the list of bitmaps
+ * @param results The list of results in which to save the brute-force search results
+ * @param signal Latch to decrement when this worker is done, may be null
+ */
+ ComputeWorker(int start, int end, int step, List<Bitmap> bitmaps, int pixelCount,
+ List<WorkerResult> results, CountDownLatch signal) {
+ mStart = start;
+ mEnd = end;
+ mStep = step;
+ mBitmaps = bitmaps;
+ mResults = results;
+ mSignal = signal;
+
+ // Minimum number of pixels we want to be able to pack
+ int threshold = (int) (pixelCount * PACKING_THRESHOLD);
+ // Make sure we can find at least one configuration
+ while (threshold > MAX_SIZE * MAX_SIZE) {
+ threshold >>= 1;
+ }
+ mThreshold = threshold;
+ }
+
+ @Override
+ public void run() {
+ if (DEBUG_ATLAS) Log.d(LOG_TAG, "Running " + Thread.currentThread().getName());
+
+ Atlas.Entry entry = new Atlas.Entry();
+ for (Atlas.Type type : Atlas.Type.values()) {
+ for (int width = mStart; width < mEnd; width += mStep) {
+ for (int height = MIN_SIZE; height < MAX_SIZE; height += STEP) {
+ // If the atlas is not big enough, skip it
+ if (width * height <= mThreshold) continue;
+
+ final int count = packBitmaps(type, width, height, entry);
+ if (count > 0) {
+ mResults.add(new WorkerResult(type, width, height, count));
+ // If we were able to pack everything let's stop here
+ // Increasing the height further won't make things better
+ if (count == mBitmaps.size()) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (mSignal != null) {
+ mSignal.countDown();
+ }
+ }
+
+ private int packBitmaps(Atlas.Type type, int width, int height, Atlas.Entry entry) {
+ int total = 0;
+ Atlas atlas = new Atlas(type, width, height);
+
+ final int count = mBitmaps.size();
+ for (int i = 0; i < count; i++) {
+ final Bitmap bitmap = mBitmaps.get(i);
+ if (atlas.pack(bitmap.getWidth(), bitmap.getHeight(), entry) != null) {
+ total++;
+ }
+ }
+
+ return total;
+ }
+ }
+}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 2a3c87e..a537e99 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -3735,7 +3735,16 @@ class BackupManagerService extends IBackupManager.Stub {
} else {
// So far so good -- do the signatures match the manifest?
Signature[] sigs = mManifestSignatures.get(info.packageName);
- if (!signaturesMatch(sigs, pkg)) {
+ if (signaturesMatch(sigs, pkg)) {
+ // If this is a system-uid app without a declared backup agent,
+ // don't restore any of the file data.
+ if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
+ && (pkg.applicationInfo.backupAgentName == null)) {
+ Slog.w(TAG, "Installed app " + info.packageName
+ + " has restricted uid and no agent");
+ okay = false;
+ }
+ } else {
Slog.w(TAG, "Installed app " + info.packageName
+ " signatures do not match restore manifest");
okay = false;
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 36fd7fc..32fc18e 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -236,6 +236,15 @@ public final class BatteryService extends Binder {
}
}
+ /**
+ * Returns a non-zero value if an unsupported charger is attached.
+ */
+ public int getInvalidCharger() {
+ synchronized (mLock) {
+ return mInvalidCharger;
+ }
+ }
+
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index ea7b696..8684e5b 100644
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -33,7 +33,6 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -115,7 +114,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
// used inside handler thread
private boolean mEnable;
private int mState;
- private HandlerThread mThread;
private final BluetoothHandler mHandler;
private void registerForAirplaneMode(IntentFilter filter) {
@@ -188,9 +186,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
};
BluetoothManagerService(Context context) {
- mThread = new HandlerThread("BluetoothManager");
- mThread.start();
- mHandler = new BluetoothHandler(mThread.getLooper());
+ mHandler = new BluetoothHandler(IoThread.get().getLooper());
mContext = context;
mBluetooth = null;
diff --git a/services/java/com/android/server/CountryDetectorService.java b/services/java/com/android/server/CountryDetectorService.java
index fc76277..8407fa4 100644
--- a/services/java/com/android/server/CountryDetectorService.java
+++ b/services/java/com/android/server/CountryDetectorService.java
@@ -20,6 +20,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
+import com.android.internal.os.BackgroundThread;
import com.android.server.location.ComprehensiveCountryDetector;
import android.content.Context;
@@ -29,8 +30,6 @@ import android.location.ICountryDetector;
import android.location.ICountryListener;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Process;
import android.os.RemoteException;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -169,8 +168,7 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
void systemReady() {
// Shall we wait for the initialization finish.
- Thread thread = new Thread(this, "CountryDetectorService");
- thread.start();
+ BackgroundThread.getHandler().post(this);
}
private void initialize() {
@@ -187,12 +185,9 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
}
public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- Looper.prepare();
mHandler = new Handler();
initialize();
mSystemReady = true;
- Looper.loop();
}
protected void setCountryListener(final CountryListener listener) {
diff --git a/services/java/com/android/server/FgThread.java b/services/java/com/android/server/FgThread.java
new file mode 100644
index 0000000..3b655f2
--- /dev/null
+++ b/services/java/com/android/server/FgThread.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 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.os.Handler;
+import android.os.HandlerThread;
+
+/**
+ * Shared singleton foreground thread for the system. This is a thread for regular
+ * foreground service operations, which shouldn't be blocked by anything running in
+ * the background. In particular, the shared background thread could be doing
+ * relatively long-running operations like saving state to disk (in addition to
+ * simply being a background priority), which can cause operations scheduled on it
+ * to be delayed for a user-noticeable amount of time.
+ */
+public final class FgThread extends HandlerThread {
+ private static FgThread sInstance;
+ private static Handler sHandler;
+
+ private FgThread() {
+ super("android.fg", android.os.Process.THREAD_PRIORITY_DEFAULT);
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new FgThread();
+ sInstance.start();
+ sHandler = new Handler(sInstance.getLooper());
+ sHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ android.os.Process.setCanSelfBackground(false);
+ }
+ });
+ }
+ }
+
+ public static FgThread get() {
+ synchronized (UiThread.class) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ public static Handler getHandler() {
+ synchronized (UiThread.class) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+}
diff --git a/services/java/com/android/server/IdleMaintenanceService.java b/services/java/com/android/server/IdleMaintenanceService.java
index 0c90de4..584d4bc 100644
--- a/services/java/com/android/server/IdleMaintenanceService.java
+++ b/services/java/com/android/server/IdleMaintenanceService.java
@@ -17,11 +17,12 @@
package com.android.server;
import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -40,7 +41,6 @@ import android.util.Log;
* The current implementation is very simple. The start of a maintenance
* window is announced if: the screen is off or showing a dream AND the
* battery level is more than twenty percent AND at least one hour passed
- * since the screen went off or a dream started (i.e. since the last user
* activity).
*
* The end of a maintenance window is announced only if: a start was
@@ -48,27 +48,44 @@ import android.util.Log;
*/
public class IdleMaintenanceService extends BroadcastReceiver {
- private final boolean DEBUG = false;
+ private static final boolean DEBUG = false;
private static final String LOG_TAG = IdleMaintenanceService.class.getSimpleName();
private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
- private static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
+ private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
- private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 10; // percent
+ private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
- private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 60 * 60 * 1000; // 1 hour
+ private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
- private final Intent mIdleMaintenanceStartIntent =
- new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
+ private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
- private final Intent mIdleMaintenanceEndIntent =
- new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
+ private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
+ "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
+
+ private static final Intent sIdleMaintenanceStartIntent;
+ static {
+ sIdleMaintenanceStartIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
+ sIdleMaintenanceStartIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ };
+
+ private static final Intent sIdleMaintenanceEndIntent;
+ static {
+ sIdleMaintenanceEndIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
+ sIdleMaintenanceEndIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ }
+
+ private final AlarmManager mAlarmService;
+
+ private final BatteryService mBatteryService;
+
+ private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
private final Context mContext;
@@ -76,30 +93,37 @@ public class IdleMaintenanceService extends BroadcastReceiver {
private final Handler mHandler;
- private long mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
+ private long mLastIdleMaintenanceStartTimeMillis;
private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
- private int mBatteryLevel;
-
- private boolean mBatteryCharging;
-
private boolean mIdleMaintenanceStarted;
- public IdleMaintenanceService(Context context) {
+ public IdleMaintenanceService(Context context, BatteryService batteryService) {
mContext = context;
+ mBatteryService = batteryService;
+
+ mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
mHandler = new Handler(mContext.getMainLooper());
+ Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
+ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
register(mContext.getMainLooper());
}
public void register(Looper looper) {
IntentFilter intentFilter = new IntentFilter();
+ // Alarm actions.
+ intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
+
// Battery actions.
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
@@ -115,67 +139,117 @@ public class IdleMaintenanceService extends BroadcastReceiver {
intentFilter, null, new Handler(looper));
}
+ private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
+ final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
+ mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
+ mUpdateIdleMaintenanceStatePendingIntent);
+ }
+
+ private void unscheduleUpdateIdleMaintenanceState() {
+ mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
+ }
+
private void updateIdleMaintenanceState() {
if (mIdleMaintenanceStarted) {
- // Idle maintenance can be interrupted only by
- // a change of the device state.
- if (!deviceStatePermitsIdleMaintenanceRunning()) {
+ // Idle maintenance can be interrupted by user activity, or duration
+ // time out, or low battery.
+ if (!lastUserActivityPermitsIdleMaintenanceRunning()
+ || !batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
+ unscheduleUpdateIdleMaintenanceState();
mIdleMaintenanceStarted = false;
EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
- mLastUserActivityElapsedTimeMillis, mBatteryLevel,
- mBatteryCharging ? 1 : 0);
+ mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
+ isBatteryCharging() ? 1 : 0);
sendIdleMaintenanceEndIntent();
+ // We stopped since we don't have enough battery or timed out but the
+ // user is not using the device, so we should be able to run maintenance
+ // in the next maintenance window since the battery may be charged
+ // without interaction and the min interval between maintenances passed.
+ if (!batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
+ scheduleUpdateIdleMaintenanceState(
+ getNextIdleMaintenanceIntervalStartFromNow());
+ }
}
} else if (deviceStatePermitsIdleMaintenanceStart()
&& lastUserActivityPermitsIdleMaintenanceStart()
&& lastRunPermitsIdleMaintenanceStart()) {
+ // Now that we started idle maintenance, we should schedule another
+ // update for the moment when the idle maintenance times out.
+ scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
mIdleMaintenanceStarted = true;
EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
- mLastUserActivityElapsedTimeMillis, mBatteryLevel,
- mBatteryCharging ? 1 : 0);
+ mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
+ isBatteryCharging() ? 1 : 0);
mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
sendIdleMaintenanceStartIntent();
+ } else if (lastUserActivityPermitsIdleMaintenanceStart()) {
+ if (lastRunPermitsIdleMaintenanceStart()) {
+ // The user does not use the device and we did not run maintenance in more
+ // than the min interval between runs, so schedule an update - maybe the
+ // battery will be charged latter.
+ scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
+ } else {
+ // The user does not use the device but we have run maintenance in the min
+ // interval between runs, so schedule an update after the min interval ends.
+ scheduleUpdateIdleMaintenanceState(
+ getNextIdleMaintenanceIntervalStartFromNow());
+ }
}
}
+ private long getNextIdleMaintenanceIntervalStartFromNow() {
+ return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
+ - SystemClock.elapsedRealtime();
+ }
+
private void sendIdleMaintenanceStartIntent() {
- if (DEBUG) {
- Log.i(LOG_TAG, "Broadcasting " + Intent.ACTION_IDLE_MAINTENANCE_START);
- }
mWakeLock.acquire();
- mContext.sendOrderedBroadcastAsUser(mIdleMaintenanceStartIntent, UserHandle.ALL,
+ mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceStartIntent, UserHandle.ALL,
null, this, mHandler, Activity.RESULT_OK, null, null);
}
private void sendIdleMaintenanceEndIntent() {
- if (DEBUG) {
- Log.i(LOG_TAG, "Broadcasting " + Intent.ACTION_IDLE_MAINTENANCE_END);
- }
mWakeLock.acquire();
- mContext.sendOrderedBroadcastAsUser(mIdleMaintenanceEndIntent, UserHandle.ALL,
+ mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceEndIntent, UserHandle.ALL,
null, this, mHandler, Activity.RESULT_OK, null, null);
}
private boolean deviceStatePermitsIdleMaintenanceStart() {
- final int minBatteryLevel = mBatteryCharging
+ final int minBatteryLevel = isBatteryCharging()
? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
: MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
- && mBatteryLevel > minBatteryLevel);
+ && mBatteryService.getBatteryLevel() > minBatteryLevel);
}
- private boolean deviceStatePermitsIdleMaintenanceRunning() {
+ private boolean lastUserActivityPermitsIdleMaintenanceStart() {
+ // The last time the user poked the device is above the threshold.
return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
- && mBatteryLevel > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING);
+ && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
+ > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
}
- private boolean lastUserActivityPermitsIdleMaintenanceStart() {
- return (SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
- > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
+ private boolean lastRunPermitsIdleMaintenanceStart() {
+ // Enough time passed since the last maintenance run.
+ return SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
+ > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
}
- private boolean lastRunPermitsIdleMaintenanceStart() {
- return SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis > MILLIS_IN_DAY;
+ private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
+ // The user is not using the device.
+ return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
+ }
+
+ private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
+ // Battery not too low and the maintenance duration did not timeout.
+ return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
+ && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
+ > SystemClock.elapsedRealtime());
+ }
+
+ private boolean isBatteryCharging() {
+ return mBatteryService.getPlugType() > 0
+ && mBatteryService.getInvalidCharger() == 0;
}
@Override
@@ -185,24 +259,38 @@ public class IdleMaintenanceService extends BroadcastReceiver {
}
String action = intent.getAction();
if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
- final int maxBatteryLevel = intent.getExtras().getInt(BatteryManager.EXTRA_SCALE);
- final int currBatteryLevel = intent.getExtras().getInt(BatteryManager.EXTRA_LEVEL);
- mBatteryLevel = (int) (((float) maxBatteryLevel / 100) * currBatteryLevel);
- final int pluggedState = intent.getExtras().getInt(BatteryManager.EXTRA_PLUGGED);
- final int chargerState = intent.getExtras().getInt(
- BatteryManager.EXTRA_INVALID_CHARGER, 0);
- mBatteryCharging = (pluggedState > 0 && chargerState == 0);
+ // We care about battery only if maintenance is in progress so we can
+ // stop it if battery is too low. Note that here we assume that the
+ // maintenance clients are properly holding a wake lock. We will
+ // refactor the maintenance to use services instead of intents for the
+ // next release. The only client for this for now is internal an holds
+ // a wake lock correctly.
+ if (mIdleMaintenanceStarted) {
+ updateIdleMaintenanceState();
+ }
} else if (Intent.ACTION_SCREEN_ON.equals(action)
|| Intent.ACTION_DREAMING_STOPPED.equals(action)) {
mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
+ // Unschedule any future updates since we already know that maintenance
+ // cannot be performed since the user is back.
+ unscheduleUpdateIdleMaintenanceState();
+ // If the screen went on/stopped dreaming, we know the user is using the
+ // device which means that idle maintenance should be stopped if running.
+ updateIdleMaintenanceState();
} else if (Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_DREAMING_STARTED.equals(action)) {
mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
+ // If screen went off/started dreaming, we may be able to start idle maintenance
+ // after the minimal user inactivity elapses. We schedule an alarm for when
+ // this timeout elapses since the device may go to sleep by then.
+ scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
+ } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
+ updateIdleMaintenanceState();
} else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
|| Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
+ // We were holding a wake lock while broadcasting the idle maintenance
+ // intents but now that we finished the broadcast release the wake lock.
mWakeLock.release();
- return;
}
- updateIdleMaintenanceState();
}
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 3baa565..6e24d68 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -705,7 +705,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public void onReceive(Context context, Intent intent) {
synchronized(mMethodMap) {
- checkCurrentLocaleChangedLocked();
+ resetStateIfCurrentLocaleChangedLocked();
}
}
}, filter);
@@ -781,7 +781,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- private void checkCurrentLocaleChangedLocked() {
+ private void resetStateIfCurrentLocaleChangedLocked() {
resetAllInternalStateLocked(true /* updateOnlyWhenLocaleChanged */,
true /* resetDefaultImeLocked */);
}
@@ -791,17 +791,21 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// InputMethodFileManager should be reset when the user is changed
mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
final String defaultImiId = mSettings.getSelectedInputMethod();
- final boolean needsToResetDefaultIme = TextUtils.isEmpty(defaultImiId);
// For secondary users, the list of enabled IMEs may not have been updated since the
// callbacks to PackageMonitor are ignored for the secondary user. Here, defaultImiId may
// not be empty even if the IME has been uninstalled by the primary user.
// Even in such cases, IMMS works fine because it will find the most applicable
// IME for that user.
+ final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId);
if (DEBUG) {
Slog.d(TAG, "Switch user: " + newUserId + " current ime = " + defaultImiId);
}
- resetAllInternalStateLocked(false /* updateOnlyWhenLocaleChanged */,
- needsToResetDefaultIme);
+ resetAllInternalStateLocked(false /* updateOnlyWhenLocaleChanged */,
+ initialUserSwitch /* needsToResetDefaultIme */);
+ if (initialUserSwitch) {
+ InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mContext.getPackageManager(),
+ mSettings.getEnabledInputMethodListLocked());
+ }
}
@Override
@@ -843,7 +847,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
!mImeSelectedOnBoot /* resetDefaultEnabledIme */);
if (!mImeSelectedOnBoot) {
Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here.");
- checkCurrentLocaleChangedLocked();
+ resetStateIfCurrentLocaleChangedLocked();
+ InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
+ mContext.getPackageManager(),
+ mSettings.getEnabledInputMethodListLocked());
}
mLastSystemLocale = mRes.getConfiguration().locale;
try {
@@ -1604,6 +1611,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mSettings.getCurrentUserId());
if (ai != null && ai.enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+ if (DEBUG) {
+ Slog.d(TAG, "Update state(" + imm.getId()
+ + "): DISABLED_UNTIL_USED -> DEFAULT");
+ }
mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(),
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(),
diff --git a/services/java/com/android/server/IoThread.java b/services/java/com/android/server/IoThread.java
new file mode 100644
index 0000000..b443578
--- /dev/null
+++ b/services/java/com/android/server/IoThread.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 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.os.Handler;
+import android.os.HandlerThread;
+
+/**
+ * Shared singleton I/O thread for the system. This is a thread for non-background
+ * service operations that can potential block briefly on network IO operations
+ * (not waiting for data itself, but communicating with network daemons).
+ */
+public final class IoThread extends HandlerThread {
+ private static IoThread sInstance;
+ private static Handler sHandler;
+
+ private IoThread() {
+ super("android.io", android.os.Process.THREAD_PRIORITY_DEFAULT);
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new IoThread();
+ sInstance.start();
+ sHandler = new Handler(sInstance.getLooper());
+ sHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ android.os.Process.setCanSelfBackground(false);
+ }
+ });
+ }
+ }
+
+ public static IoThread get() {
+ synchronized (UiThread.class) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ public static Handler getHandler() {
+ synchronized (UiThread.class) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 2675309..016a664 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -63,6 +63,7 @@ import android.util.Slog;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
+import com.android.internal.os.BackgroundThread;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceProxy;
import com.android.server.location.GeofenceManager;
@@ -93,7 +94,6 @@ public class LocationManagerService extends ILocationManager.Stub {
public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
private static final String WAKELOCK_KEY = TAG;
- private static final String THREAD_NAME = TAG;
// Location resolution level: no location data whatsoever
private static final int RESOLUTION_LEVEL_NONE = 0;
@@ -143,7 +143,6 @@ public class LocationManagerService extends ILocationManager.Stub {
private LocationWorkerHandler mLocationHandler;
private PassiveProvider mPassiveProvider; // track passive provider for special cases
private LocationBlacklist mBlacklist;
- private HandlerThread mHandlerThread;
// --- fields below are protected by mWakeLock ---
private int mPendingBroadcasts;
@@ -216,9 +215,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
// prepare worker thread
- mHandlerThread = new HandlerThread(THREAD_NAME, Process.THREAD_PRIORITY_BACKGROUND);
- mHandlerThread.start();
- mLocationHandler = new LocationWorkerHandler(mHandlerThread.getLooper());
+ mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
// prepare mLocationHandler's dependents
mLocationFudger = new LocationFudger(mContext, mLocationHandler);
@@ -1798,7 +1795,7 @@ public class LocationManagerService extends ILocationManager.Stub {
boolean receiverDead = false;
int receiverUserId = UserHandle.getUserId(receiver.mUid);
- if (receiverUserId != mCurrentUserId) {
+ if (receiverUserId != mCurrentUserId && !isUidALocationProvider(receiver.mUid)) {
if (D) {
Log.d(TAG, "skipping loc update for background user " + receiverUserId +
" (current user: " + mCurrentUserId + ", app: " +
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d7adbf7..1d1b6b9 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -37,7 +37,6 @@ import android.os.Binder;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -491,7 +490,6 @@ class MountService extends IMountService.Stub
}
};
- private final HandlerThread mHandlerThread;
private final Handler mHandler;
void waitForAsecScan() {
@@ -827,7 +825,7 @@ class MountService extends IMountService.Stub
}
if (code == VoldResponseCode.VolumeDiskInserted) {
- new Thread() {
+ new Thread("MountService#VolumeDiskInserted") {
@Override
public void run() {
try {
@@ -1114,7 +1112,7 @@ class MountService extends IMountService.Stub
/*
* USB mass storage disconnected while enabled
*/
- new Thread() {
+ new Thread("MountService#AvailabilityChange") {
@Override
public void run() {
try {
@@ -1313,9 +1311,7 @@ class MountService extends IMountService.Stub
// XXX: This will go away soon in favor of IMountServiceObserver
mPms = (PackageManagerService) ServiceManager.getService("package");
- mHandlerThread = new HandlerThread("MountService");
- mHandlerThread.start();
- mHandler = new MountServiceHandler(mHandlerThread.getLooper());
+ mHandler = new MountServiceHandler(IoThread.get().getLooper());
// Watch for user changes
final IntentFilter userFilter = new IntentFilter();
@@ -1337,7 +1333,7 @@ class MountService extends IMountService.Stub
idleMaintenanceFilter, null, mHandler);
// Add OBB Action Handler to MountService thread.
- mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
+ mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
/*
* Create the connection to vold with a maximum queue of twice the
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index c3f2afa..abcd8ee 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -19,7 +19,6 @@ package com.android.server;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
import android.util.LocalLog;
@@ -81,9 +80,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
@Override
public void run() {
- HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
- thread.start();
- mCallbackHandler = new Handler(thread.getLooper(), this);
+ mCallbackHandler = new Handler(FgThread.get().getLooper(), this);
while (true) {
try {
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
index 3bfd190..02b42b8 100644
--- a/services/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -27,7 +27,6 @@ import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
@@ -36,6 +35,7 @@ import android.util.Log;
import android.util.NtpTrustedTime;
import android.util.TrustedTime;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.TelephonyIntents;
/**
@@ -71,7 +71,6 @@ public class NetworkTimeUpdateService {
// NTP lookup is done on this thread and handler
private Handler mHandler;
- private HandlerThread mThread;
private AlarmManager mAlarmManager;
private PendingIntent mPendingPollIntent;
private SettingsObserver mSettingsObserver;
@@ -114,9 +113,7 @@ public class NetworkTimeUpdateService {
registerForAlarms();
registerForConnectivityIntents();
- mThread = new HandlerThread(TAG);
- mThread.start();
- mHandler = new MyHandler(mThread.getLooper());
+ mHandler = new MyHandler(BackgroundThread.get().getLooper());
// Check the network time on the new thread
mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 92f72ba..89a6f60 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -155,30 +155,6 @@ class ServerThread extends Thread {
InputManagerService inputManager = null;
TelephonyRegistry telephonyRegistry = null;
- // Create a shared handler thread for UI within the system server.
- // This thread is used by at least the following components:
- // - WindowManagerPolicy
- // - KeyguardViewManager
- // - DisplayManagerService
- HandlerThread uiHandlerThread = new HandlerThread("UI");
- uiHandlerThread.start();
- Handler uiHandler = new Handler(uiHandlerThread.getLooper());
- uiHandler.post(new Runnable() {
- @Override
- public void run() {
- //Looper.myLooper().setMessageLogging(new LogPrinter(
- // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_FOREGROUND);
- android.os.Process.setCanSelfBackground(false);
-
- // For debug builds, log event loop stalls to dropbox for analysis.
- if (StrictMode.conditionallyEnableDebugLogging()) {
- Slog.i(TAG, "Enabled StrictMode logging for UI Looper");
- }
- }
- });
-
// Create a handler thread just for the window manager to enjoy.
HandlerThread wmHandlerThread = new HandlerThread("WindowManager");
wmHandlerThread.start();
@@ -232,7 +208,7 @@ class ServerThread extends Thread {
try {
Slog.i(TAG, "Display Manager");
- display = new DisplayManagerService(context, wmHandler, uiHandler);
+ display = new DisplayManagerService(context, wmHandler);
ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
Slog.i(TAG, "Telephony Registry");
@@ -320,14 +296,14 @@ class ServerThread extends Thread {
Slog.i(TAG, "Init Watchdog");
Watchdog.getInstance().init(context, battery, power, alarm,
ActivityManagerService.self());
+ Watchdog.getInstance().addThread(wmHandler, "WindowManager thread");
Slog.i(TAG, "Input Manager");
inputManager = new InputManagerService(context, wmHandler);
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power, display, inputManager,
- uiHandler, wmHandler,
- factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
+ wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
@@ -373,6 +349,7 @@ class ServerThread extends Thread {
TextServicesManagerService tsms = null;
LockSettingsService lockSettings = null;
DreamManagerService dreamy = null;
+ AssetAtlasService atlas = null;
// Bring up services needed for UI.
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
@@ -796,9 +773,19 @@ class ServerThread extends Thread {
}
}
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Assets Atlas Service");
+ atlas = new AssetAtlasService(context);
+ ServiceManager.addService(AssetAtlasService.ASSET_ATLAS_SERVICE, atlas);
+ } catch (Throwable e) {
+ reportWtf("starting AssetAtlasService", e);
+ }
+ }
+
try {
Slog.i(TAG, "IdleMaintenanceService");
- new IdleMaintenanceService(context);
+ new IdleMaintenanceService(context, battery);
} catch (Throwable e) {
reportWtf("starting IdleMaintenanceService", e);
}
@@ -910,6 +897,7 @@ class ServerThread extends Thread {
final TextServicesManagerService textServiceManagerServiceF = tsms;
final StatusBarManagerService statusBarF = statusBar;
final DreamManagerService dreamyF = dreamy;
+ final AssetAtlasService atlasF = atlas;
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
@@ -1036,6 +1024,11 @@ class ServerThread extends Thread {
reportWtf("making DreamManagerService ready", e);
}
try {
+ if (atlasF != null) atlasF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making AssetAtlasService ready", e);
+ }
+ try {
// TODO(BT) Pass parameter to input manager
if (inputManagerF != null) inputManagerF.systemReady();
} catch (Throwable e) {
diff --git a/services/java/com/android/server/UiThread.java b/services/java/com/android/server/UiThread.java
new file mode 100644
index 0000000..60d73aa
--- /dev/null
+++ b/services/java/com/android/server/UiThread.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 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.os.Handler;
+import android.os.HandlerThread;
+import android.os.StrictMode;
+import android.util.Slog;
+
+/**
+ * Shared singleton thread for showing UI. This is a foreground thread, and in
+ * additional should not have operations that can take more than a few ms scheduled
+ * on it to avoid UI jank.
+ */
+public final class UiThread extends HandlerThread {
+ private static UiThread sInstance;
+ private static Handler sHandler;
+
+ private UiThread() {
+ super("android.ui", android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new UiThread();
+ sInstance.start();
+ sHandler = new Handler(sInstance.getLooper());
+ sHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ //Looper.myLooper().setMessageLogging(new LogPrinter(
+ // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
+ android.os.Process.setCanSelfBackground(false);
+
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i("UiThread", "Enabled StrictMode logging for UI thread");
+ }
+ }
+ });
+ }
+ }
+
+ public static UiThread get() {
+ synchronized (UiThread.class) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ public static Handler getHandler() {
+ synchronized (UiThread.class) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+}
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index ace543b..551d9f6 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -16,6 +16,9 @@
package com.android.server;
+import android.app.IActivityController;
+import android.os.Binder;
+import android.os.RemoteException;
import com.android.server.am.ActivityManagerService;
import com.android.server.power.PowerManagerService;
@@ -80,17 +83,17 @@ public class Watchdog extends Thread {
static Watchdog sWatchdog;
/* This handler will be used to post message back onto the main thread */
- final Handler mHandler;
- final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
+ final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<HandlerChecker>();
+ final HandlerChecker mMonitorChecker;
ContentResolver mResolver;
BatteryService mBattery;
PowerManagerService mPower;
AlarmManagerService mAlarm;
ActivityManagerService mActivity;
- boolean mCompleted;
- Monitor mCurrentMonitor;
int mPhonePid;
+ IActivityController mController;
+ boolean mAllowRestart = true;
final Calendar mCalendar = Calendar.getInstance();
int mMinScreenOff = MEMCHECK_DEFAULT_MIN_SCREEN_OFF;
@@ -111,40 +114,65 @@ public class Watchdog extends Thread {
int mReqRecheckInterval= -1; // >= 0 if a specific recheck interval has been requested
/**
- * Used for scheduling monitor callbacks and checking memory usage.
+ * Used for checking status of handle threads and scheduling monitor callbacks.
*/
- final class HeartbeatHandler extends Handler {
- HeartbeatHandler(Looper looper) {
- super(looper);
+ public final class HandlerChecker implements Runnable {
+ private final Handler mHandler;
+ private final String mName;
+ private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
+ private final boolean mCheckReboot;
+ private boolean mCompleted;
+ private Monitor mCurrentMonitor;
+
+ HandlerChecker(Handler handler, String name, boolean checkReboot) {
+ mHandler = handler;
+ mName = name;
+ mCheckReboot = checkReboot;
+ }
+
+ public void addMonitor(Monitor monitor) {
+ mMonitors.add(monitor);
+ }
+
+ public void scheduleCheckLocked() {
+ mCompleted = false;
+ mCurrentMonitor = null;
+ mHandler.postAtFrontOfQueue(this);
+ }
+
+ public boolean isCompletedLocked() {
+ return mCompleted;
+ }
+
+ public String describeBlockedStateLocked() {
+ return mCurrentMonitor == null ? mName : mCurrentMonitor.getClass().getName();
}
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MONITOR: {
- // See if we should force a reboot.
- int rebootInterval = mReqRebootInterval >= 0
- ? mReqRebootInterval : REBOOT_DEFAULT_INTERVAL;
- if (mRebootInterval != rebootInterval) {
- mRebootInterval = rebootInterval;
- // We have been running long enough that a reboot can
- // be considered...
- checkReboot(false);
- }
+ public void run() {
+ // See if we should force a reboot.
+ if (mCheckReboot) {
+ int rebootInterval = mReqRebootInterval >= 0
+ ? mReqRebootInterval : REBOOT_DEFAULT_INTERVAL;
+ if (mRebootInterval != rebootInterval) {
+ mRebootInterval = rebootInterval;
+ // We have been running long enough that a reboot can
+ // be considered...
+ checkReboot(false);
+ }
+ }
- final int size = mMonitors.size();
- for (int i = 0 ; i < size ; i++) {
- synchronized (Watchdog.this) {
- mCurrentMonitor = mMonitors.get(i);
- }
- mCurrentMonitor.monitor();
- }
+ final int size = mMonitors.size();
+ for (int i = 0 ; i < size ; i++) {
+ synchronized (Watchdog.this) {
+ mCurrentMonitor = mMonitors.get(i);
+ }
+ mCurrentMonitor.monitor();
+ }
- synchronized (Watchdog.this) {
- mCompleted = true;
- mCurrentMonitor = null;
- }
- } break;
+ synchronized (Watchdog.this) {
+ mCompleted = true;
+ mCurrentMonitor = null;
}
}
}
@@ -189,9 +217,23 @@ public class Watchdog extends Thread {
private Watchdog() {
super("watchdog");
- // Explicitly bind the HeartbeatHandler to run on the ServerThread, so
- // that it can't get accidentally bound to another thread.
- mHandler = new HeartbeatHandler(Looper.getMainLooper());
+ // Initialize handler checkers for each common thread we want to check. Note
+ // that we are not currently checking the background thread, since it can
+ // potentially hold longer running operations with no guarantees about the timeliness
+ // of operations there.
+
+ // The shared foreground thread is the main checker. It is where we
+ // will also dispatch monitor checks and do other work.
+ mMonitorChecker = new HandlerChecker(FgThread.getHandler(), "foreground thread", true);
+ mHandlerCheckers.add(mMonitorChecker);
+ // Add checker for main thread. We only do a quick check since there
+ // can be UI running on the thread.
+ mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
+ "main thread", false));
+ // Add checker for shared UI thread.
+ mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(), "ui thread", false));
+ // And also check IO thread.
+ mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(), "i/o thread", false));
}
public void init(Context context, BatteryService battery,
@@ -223,12 +265,33 @@ public class Watchdog extends Thread {
}
}
+ public void setActivityController(IActivityController controller) {
+ synchronized (this) {
+ mController = controller;
+ }
+ }
+
+ public void setAllowRestart(boolean allowRestart) {
+ synchronized (this) {
+ mAllowRestart = allowRestart;
+ }
+ }
+
public void addMonitor(Monitor monitor) {
synchronized (this) {
if (isAlive()) {
- throw new RuntimeException("Monitors can't be added while the Watchdog is running");
+ throw new RuntimeException("Monitors can't be added once the Watchdog is running");
}
- mMonitors.add(monitor);
+ mMonitorChecker.addMonitor(monitor);
+ }
+ }
+
+ public void addThread(Handler thread, String name) {
+ synchronized (this) {
+ if (isAlive()) {
+ throw new RuntimeException("Threads can't be added once the Watchdog is running");
+ }
+ mHandlerCheckers.add(new HandlerChecker(thread, name, false));
}
}
@@ -382,15 +445,46 @@ public class Watchdog extends Thread {
return newTime;
}
+ private boolean haveAllCheckersCompletedLocked() {
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ if (!hc.isCompletedLocked()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private String describeBlockedCheckersLocked() {
+ StringBuilder builder = new StringBuilder(128);
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ if (!hc.isCompletedLocked()) {
+ if (builder.length() > 0) {
+ builder.append(", ");
+ }
+ builder.append(hc.describeBlockedStateLocked());
+ }
+ }
+ return builder.toString();
+ }
+
@Override
public void run() {
boolean waitedHalf = false;
while (true) {
final String name;
+ final boolean allowRestart;
synchronized (this) {
long timeout = TIME_TO_WAIT;
- mCompleted = false;
- mHandler.sendEmptyMessage(MONITOR);
+ if (!waitedHalf) {
+ // If we are not at the half-point of waiting, perform a
+ // new set of checks. Otherwise we are still waiting for a previous set.
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ hc.scheduleCheckLocked();
+ }
+ }
// NOTE: We use uptimeMillis() here because we do not want to increment the time we
// wait while asleep. If the device is asleep then the thing that we are waiting
@@ -406,7 +500,7 @@ public class Watchdog extends Thread {
timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start);
}
- if (mCompleted) {
+ if (haveAllCheckersCompletedLocked()) {
// The monitors have returned.
waitedHalf = false;
continue;
@@ -423,8 +517,8 @@ public class Watchdog extends Thread {
continue;
}
- name = (mCurrentMonitor != null) ?
- mCurrentMonitor.getClass().getName() : "main thread blocked";
+ name = describeBlockedCheckersLocked();
+ allowRestart = mAllowRestart;
}
// If we got here, that means that the system is most likely hung.
@@ -474,19 +568,40 @@ public class Watchdog extends Thread {
dropboxThread.join(2000); // wait up to 2 seconds for it to return.
} catch (InterruptedException ignored) {}
+ IActivityController controller;
+ synchronized (this) {
+ controller = mController;
+ }
+ if (controller != null) {
+ Slog.i(TAG, "Reporting stuck state to activity controller");
+ try {
+ Binder.setDumpDisabled("Service dumps disabled due to hung system process.");
+ // 1 = keep waiting, -1 = kill system
+ int res = controller.systemNotResponding(name);
+ if (res >= 0) {
+ Slog.i(TAG, "Activity controller requested to coninue to wait");
+ waitedHalf = false;
+ continue;
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
// Only kill the process if the debugger is not attached.
- if (!Debug.isDebuggerConnected()) {
+ if (Debug.isDebuggerConnected()) {
+ Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
+ } else if (!allowRestart) {
+ Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
+ } else {
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name);
Slog.w(TAG, "Main thread stack trace:");
- StackTraceElement[] stackTrace = mHandler.getLooper().getThread().getStackTrace();
+ StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace();
for (StackTraceElement element: stackTrace) {
Slog.w(TAG, "\tat " + element);
}
Slog.w(TAG, "<End of main thread stack trace>");
Process.killProcess(Process.myPid());
System.exit(10);
- } else {
- Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
}
waitedHalf = false;
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 138c51b..2b5544b 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1375,29 +1375,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private void updateTouchExplorationLocked(UserState userState) {
- userState.mIsTouchExplorationEnabled = false;
+ boolean enabled = false;
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
Service service = userState.mBoundServices.get(i);
- if (tryEnableTouchExplorationLocked(service)) {
+ if (canRequestAndRequestsTouchExplorationLocked(service)) {
+ enabled = true;
break;
}
}
+ if (enabled != userState.mIsTouchExplorationEnabled) {
+ userState.mIsTouchExplorationEnabled = enabled;
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TOUCH_EXPLORATION_ENABLED, enabled ? 1 : 0,
+ userState.mUserId);
+ }
}
- private boolean tryEnableTouchExplorationLocked(Service service) {
+ private boolean canRequestAndRequestsTouchExplorationLocked(Service service) {
+ // Service not ready or cannot request the feature - well nothing to do.
if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) {
return false;
}
- UserState userState = getUserStateLocked(service.mUserId);
- if (userState.mIsTouchExplorationEnabled) {
- return false;
- }
// UI test automation service can always enable it.
if (service.mIsAutomation) {
- userState.mIsTouchExplorationEnabled = true;
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, service.mUserId);
return true;
}
if (service.mResolveInfo.serviceInfo.applicationInfo.targetSdkVersion
@@ -1405,29 +1406,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// Up to JB-MR1 we had a white list with services that can enable touch
// exploration. When a service is first started we show a dialog to the
// use to get a permission to white list the service.
- if (!userState.mTouchExplorationGrantedServices.contains(service.mComponentName)) {
- if (mEnableTouchExplorationDialog == null
- || (mEnableTouchExplorationDialog != null
- && !mEnableTouchExplorationDialog.isShowing())) {
- mMainHandler.obtainMessage(
- MainHandler.MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG,
- service).sendToTarget();
- }
- } else {
- userState.mIsTouchExplorationEnabled = true;
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, service.mUserId);
+ UserState userState = getUserStateLocked(service.mUserId);
+ if (userState.mTouchExplorationGrantedServices.contains(service.mComponentName)) {
return true;
+ } else if (mEnableTouchExplorationDialog == null
+ || !mEnableTouchExplorationDialog.isShowing()) {
+ mMainHandler.obtainMessage(
+ MainHandler.MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG,
+ service).sendToTarget();
}
} else {
// Starting in JB-MR2 we request an accessibility service to declare
// certain capabilities in its meta-data to allow it to enable the
// corresponding features.
- if (service.mIsAutomation || (service.mAccessibilityServiceInfo.getCapabilities()
+ if ((service.mAccessibilityServiceInfo.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION) != 0) {
- userState.mIsTouchExplorationEnabled = true;
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, service.mUserId);
return true;
}
}
@@ -1435,29 +1428,29 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private void updateEnhancedWebAccessibilityLocked(UserState userState) {
- userState.mIsEnhancedWebAccessibilityEnabled = false;
+ boolean enabled = false;
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
Service service = userState.mBoundServices.get(i);
- if (tryEnableEnhancedWebAccessibilityLocked(service)) {
- return;
+ if (canRequestAndRequestsEnhancedWebAccessibilityLocked(service)) {
+ enabled = true;
+ break;
}
}
+ if (enabled != userState.mIsEnhancedWebAccessibilityEnabled) {
+ userState.mIsEnhancedWebAccessibilityEnabled = enabled;
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, enabled ? 1 : 0,
+ userState.mUserId);
+ }
}
- private boolean tryEnableEnhancedWebAccessibilityLocked(Service service) {
+ private boolean canRequestAndRequestsEnhancedWebAccessibilityLocked(Service service) {
if (!service.canReceiveEventsLocked() || !service.mRequestEnhancedWebAccessibility ) {
return false;
}
- UserState userState = getUserStateLocked(service.mUserId);
- if (userState.mIsEnhancedWebAccessibilityEnabled) {
- return false;
- }
if (service.mIsAutomation || (service.mAccessibilityServiceInfo.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY) != 0) {
- userState.mIsEnhancedWebAccessibilityEnabled = true;
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 1, userState.mUserId);
return true;
}
return false;
@@ -1711,6 +1704,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();
+ boolean mWasConnectedAndDied;
+
// Handler only for dispatching accessibility events since we use event
// types as message types allowing us to remove messages per event type.
public Handler mEventDispatchHandler = new Handler(mMainHandler.getLooper()) {
@@ -1865,8 +1860,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
UserState userState = getUserStateLocked(mUserId);
addServiceLocked(this, userState);
- if (userState.mBindingServices.contains(mComponentName)) {
+ if (userState.mBindingServices.contains(mComponentName) || mWasConnectedAndDied) {
userState.mBindingServices.remove(mComponentName);
+ mWasConnectedAndDied = false;
try {
mServiceInterface.setConnection(this, mId);
onUserStateChangedLocked(userState);
@@ -2220,7 +2216,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mServiceInterface = null;
}
- public boolean isInitializedLocked() {
+ public boolean isConnectedLocked() {
return (mService != null);
}
@@ -2230,9 +2226,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// whose handling the death recipient is unlinked and still get a call
// on binderDied since the call was made before we unlink but was
// waiting on the lock we held during the force stop handling.
- if (!isInitializedLocked()) {
+ if (!isConnectedLocked()) {
return;
}
+ mWasConnectedAndDied = true;
mKeyEventDispatcher.flush();
UserState userState = getUserStateLocked(mUserId);
// The death recipient is unregistered in removeServiceLocked
@@ -2245,7 +2242,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
userState.mEnabledServices.remove(mComponentName);
userState.destroyUiAutomationService();
}
- onUserStateChangedLocked(userState);
}
}
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 2e8d6df..d1236c1 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -56,7 +56,6 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -74,6 +73,7 @@ import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.FgThread;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
@@ -113,7 +113,6 @@ public class AccountManagerService
private final PackageManager mPackageManager;
private UserManager mUserManager;
- private HandlerThread mMessageThread;
private final MessageHandler mMessageHandler;
// Messages that can be sent on mHandler
@@ -234,9 +233,7 @@ public class AccountManagerService
mContext = context;
mPackageManager = packageManager;
- mMessageThread = new HandlerThread("AccountManagerService");
- mMessageThread.start();
- mMessageHandler = new MessageHandler(mMessageThread.getLooper());
+ mMessageHandler = new MessageHandler(FgThread.get().getLooper());
mAuthenticatorCache = authenticatorCache;
mAuthenticatorCache.setListener(this, null /* Handler */);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 41e09fe..fad1bc5 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -2871,7 +2871,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (config != null) {
r.frozenBeforeDestroy = true;
if (!updateConfigurationLocked(config, r, false, false)) {
- r.task.stack.resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
}
}
Binder.restoreCallingIdentity(origId);
@@ -2920,6 +2920,7 @@ public final class ActivityManagerService extends ActivityManagerNative
resumeOK = mController.activityResuming(next.packageName);
} catch (RemoteException e) {
mController = null;
+ Watchdog.getInstance().setActivityController(null);
}
if (!resumeOK) {
@@ -3413,6 +3414,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (res < 0 && app.pid != MY_PID) Process.killProcess(app.pid);
} catch (RemoteException e) {
mController = null;
+ Watchdog.getInstance().setActivityController(null);
}
}
@@ -3516,6 +3518,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
} catch (RemoteException e) {
mController = null;
+ Watchdog.getInstance().setActivityController(null);
}
}
@@ -4464,7 +4467,8 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
- ActivityRecord r = stack.activityIdleInternalLocked(token, false, config);
+ ActivityRecord r =
+ mStackSupervisor.activityIdleInternalLocked(token, false, config);
if (stopProfiling) {
if ((mProfileProc == r.app) && (mProfileFd != null)) {
try {
@@ -4597,7 +4601,7 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
- stack.activityResumedLocked(token);
+ ActivityRecord.activityResumedLocked(token);
}
}
Binder.restoreCallingIdentity(origId);
@@ -7569,6 +7573,7 @@ public final class ActivityManagerService extends ActivityManagerNative
"setActivityController()");
synchronized (this) {
mController = controller;
+ Watchdog.getInstance().setActivityController(controller);
}
}
@@ -7939,6 +7944,45 @@ public final class ActivityManagerService extends ActivityManagerNative
return killed;
}
+ @Override
+ public void hang(final IBinder who, boolean allowRestart) {
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+
+ final IBinder.DeathRecipient death = new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+ };
+
+ try {
+ who.linkToDeath(death, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "hang: given caller IBinder is already dead.");
+ return;
+ }
+
+ synchronized (this) {
+ Watchdog.getInstance().setAllowRestart(allowRestart);
+ Slog.i(TAG, "Hanging system process at request of pid " + Binder.getCallingPid());
+ synchronized (death) {
+ while (who.isBinderAlive()) {
+ try {
+ death.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ Watchdog.getInstance().setAllowRestart(true);
+ }
+ }
+
public final void startRunning(String pkg, String cls, String action,
String data) {
synchronized(this) {
@@ -8932,6 +8976,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
} catch (RemoteException e) {
mController = null;
+ Watchdog.getInstance().setActivityController(null);
}
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 88bcdab..51b9984 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -54,6 +54,9 @@ import java.util.HashSet;
* An entry in the history stack, representing an activity.
*/
final class ActivityRecord {
+ static final String TAG = ActivityManagerService.TAG;
+ static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE;
+
final ActivityManagerService service; // owner
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
@@ -295,10 +298,7 @@ final class ActivityRecord {
@Override public boolean keyDispatchingTimedOut() {
ActivityRecord activity = weakActivity.get();
- if (activity != null) {
- return activity.keyDispatchingTimedOut();
- }
- return false;
+ return activity != null && activity.keyDispatchingTimedOut();
}
@Override public long getKeyDispatchingTimeout() {
@@ -417,10 +417,10 @@ final class ActivityRecord {
if (intent != null && (aInfo.flags & ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS) != 0) {
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
-
+
packageName = aInfo.applicationInfo.packageName;
launchMode = aInfo.launchMode;
-
+
AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
realTheme, com.android.internal.R.styleable.Window);
fullscreen = ent != null && !ent.array.getBoolean(
@@ -429,30 +429,24 @@ final class ActivityRecord {
com.android.internal.R.styleable.Window_windowIsTranslucent, false);
noDisplay = ent != null && ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
-
- if (!_componentSpecified || _launchedFromUid == Process.myUid()
- || _launchedFromUid == 0) {
- // If we know the system has determined the component, then
- // we can consider this to be a home activity...
- if (Intent.ACTION_MAIN.equals(_intent.getAction()) &&
- _intent.hasCategory(Intent.CATEGORY_HOME) &&
- _intent.getCategories().size() == 1 &&
- _intent.getData() == null &&
- _intent.getType() == null &&
- (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
- !ResolverActivity.class.getName().equals(realActivity.getClassName())) {
+
+ // If we know the system has determined the component, then
+ // we can consider this to be a home activity...
+ // Note the last check is so we don't count the resolver
+ // activity as being home... really, we don't care about
+ // doing anything special with something that comes from
+ // the core framework package.
+ isHomeActivity =
+ (!_componentSpecified || _launchedFromUid == Process.myUid()
+ || _launchedFromUid == 0) &&
+ Intent.ACTION_MAIN.equals(_intent.getAction()) &&
+ _intent.hasCategory(Intent.CATEGORY_HOME) &&
+ _intent.getCategories().size() == 1 &&
+ _intent.getData() == null &&
+ _intent.getType() == null &&
+ (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
+ !ResolverActivity.class.getName().equals(realActivity.getClassName());
// This sure looks like a home activity!
- // Note the last check is so we don't count the resolver
- // activity as being home... really, we don't care about
- // doing anything special with something that comes from
- // the core framework package.
- isHomeActivity = true;
- } else {
- isHomeActivity = false;
- }
- } else {
- isHomeActivity = false;
- }
immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0;
} else {
@@ -580,7 +574,7 @@ final class ActivityRecord {
}
newIntents.add(intent);
}
-
+
/**
* Deliver a new Intent to an existing activity, so that its onNewIntent()
* method will be called at the proper time.
@@ -714,9 +708,6 @@ final class ActivityRecord {
}
void updateThumbnail(Bitmap newThumbnail, CharSequence description) {
- if ((intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
- // This is a logical break in the task; it repre
- }
if (thumbHolder != null) {
if (newThumbnail != null) {
if (ActivityManagerService.DEBUG_THUMBNAILS) Slog.i(ActivityManagerService.TAG,
@@ -764,20 +755,20 @@ final class ActivityRecord {
// so it is best to leave as-is.
return app != null && !app.crashing && !app.notResponding;
}
-
+
public void startFreezingScreenLocked(ProcessRecord app, int configChanges) {
if (mayFreezeScreenLocked(app)) {
service.mWindowManager.startAppFreezingScreen(appToken, configChanges);
}
}
-
+
public void stopFreezingScreenLocked(boolean force) {
if (force || frozenBeforeDestroy) {
frozenBeforeDestroy = false;
service.mWindowManager.stopAppFreezingScreen(appToken, force);
}
}
-
+
public void windowsDrawn() {
synchronized(service) {
if (launchTime != 0) {
@@ -817,7 +808,6 @@ final class ActivityRecord {
public void windowsVisible() {
synchronized(service) {
- final ActivityStack stack = task.stack;
mStackSupervisor.reportActivityVisibleLocked(this);
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG, "windowsVisible(): " + this);
@@ -858,7 +848,7 @@ final class ActivityRecord {
ActivityManagerService.TAG, "windowsGone(): " + this);
nowVisible = false;
}
-
+
private ActivityRecord getWaitingHistoryRecordLocked() {
// First find the real culprit... if we are waiting
// for another app to start, then we have paused dispatching
@@ -876,7 +866,7 @@ final class ActivityRecord {
r = this;
}
}
-
+
return r;
}
@@ -889,7 +879,7 @@ final class ActivityRecord {
}
return service.inputDispatchingTimedOut(anrApp, r, this, false);
}
-
+
/** Returns the key dispatching timeout for this application token. */
public long getKeyDispatchingTimeout() {
synchronized(service) {
@@ -903,7 +893,7 @@ final class ActivityRecord {
* currently pausing, or is resumed.
*/
public boolean isInterestingToUserLocked() {
- return visible || nowVisible || state == ActivityState.PAUSING ||
+ return visible || nowVisible || state == ActivityState.PAUSING ||
state == ActivityState.RESUMED;
}
@@ -926,6 +916,13 @@ final class ActivityRecord {
}
}
+ static void activityResumedLocked(IBinder token) {
+ final ActivityRecord r = ActivityRecord.forToken(token);
+ if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; dropping state of: " + r);
+ r.icicle = null;
+ r.haveState = false;
+ }
+
static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
final ActivityRecord r = ActivityRecord.forToken(token);
if (r == null) {
@@ -947,7 +944,7 @@ final class ActivityRecord {
return null;
}
- static final ActivityStack getStackLocked(IBinder token) {
+ static ActivityStack getStackLocked(IBinder token) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
return r.task.stack;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 38ecf34..e39ea4a 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -20,6 +20,7 @@ import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.util.Objects;
+import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.wm.AppTransition;
import com.android.server.wm.TaskGroup;
@@ -244,10 +245,9 @@ final class ActivityStack {
static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
static final int LAUNCH_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
- static final int RESUME_TOP_ACTIVITY_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
- static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
- static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
- static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 7;
+ static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
+ static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
+ static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
static class ScheduleDestroyArgs {
final ProcessRecord mOwner;
@@ -292,8 +292,7 @@ final class ActivityStack {
mService.logAppTooSlow(r.app, r.pauseTime,
"pausing " + r);
}
-
- activityPausedLocked(r != null ? r.appToken : null, true);
+ activityPausedLocked(r.appToken, true);
}
} break;
case LAUNCH_TICK_MSG: {
@@ -328,11 +327,6 @@ final class ActivityStack {
}
}
} break;
- case RESUME_TOP_ACTIVITY_MSG: {
- synchronized (mService) {
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
- }
- } break;
case STOP_TIMEOUT_MSG: {
ActivityRecord r = (ActivityRecord)msg.obj;
// We don't at this point know if the activity is fullscreen,
@@ -587,7 +581,7 @@ final class ActivityStack {
* matters on the home stack. All other stacks are single user.
* @return whether there are any activities for the specified user.
*/
- final boolean switchUserLocked(int userId, UserStartedState uss) {
+ final boolean switchUserLocked(int userId) {
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
@@ -598,10 +592,9 @@ final class ActivityStack {
// Move userId's tasks to the top.
boolean haveActivities = false;
- TaskRecord task = null;
int index = mTaskHistory.size();
for (int i = 0; i < index; ++i) {
- task = mTaskHistory.get(i);
+ TaskRecord task = mTaskHistory.get(i);
if (task.userId == userId) {
haveActivities = true;
mTaskHistory.remove(i);
@@ -774,7 +767,7 @@ final class ActivityStack {
if (prev == null) {
Slog.e(TAG, "Trying to pause when nothing is resumed",
new RuntimeException("here").fillInStackTrace());
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
return;
}
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
@@ -846,13 +839,6 @@ final class ActivityStack {
}
}
- final void activityResumedLocked(IBinder token) {
- final ActivityRecord r = ActivityRecord.forToken(token);
- if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; dropping state of: " + r);
- r.icicle = null;
- r.haveState = false;
- }
-
final void activityPausedLocked(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(
TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
@@ -900,7 +886,7 @@ final class ActivityStack {
} else {
if (r.configDestroy) {
destroyActivityLocked(r, true, false, "stop-config");
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
} else {
// Now that this process has stopped, we may want to consider
// it to be the previous app to try to keep around in case
@@ -969,7 +955,7 @@ final class ActivityStack {
final ActivityStack topStack = mStackSupervisor.getFocusedStack();
if (!mService.isSleepingOrShuttingDown()) {
- topStack.resumeTopActivityLocked(prev);
+ mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
} else {
checkReadyForSleepLocked();
ActivityRecord top = topStack.topRunningActivityLocked(null);
@@ -979,34 +965,34 @@ final class ActivityStack {
// activity on the stack is not the just paused activity,
// we need to go ahead and resume it to ensure we complete
// an in-flight app switch.
- topStack.resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
}
}
if (prev != null) {
prev.resumeKeyDispatchingLocked();
- }
- if (prev.app != null && prev.cpuTimeAtResume > 0
- && mService.mBatteryStatsService.isOnBattery()) {
- long diff = 0;
- synchronized (mService.mProcessStatsThread) {
- diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid)
- - prev.cpuTimeAtResume;
- }
- if (diff > 0) {
- BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
- synchronized (bsi) {
- BatteryStatsImpl.Uid.Proc ps =
- bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
- prev.info.packageName);
- if (ps != null) {
- ps.addForegroundTimeLocked(diff);
+ if (prev.app != null && prev.cpuTimeAtResume > 0
+ && mService.mBatteryStatsService.isOnBattery()) {
+ long diff;
+ synchronized (mService.mProcessStatsThread) {
+ diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid)
+ - prev.cpuTimeAtResume;
+ }
+ if (diff > 0) {
+ BatteryStatsImpl bsi = mService.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
}
- prev.cpuTimeAtResume = 0; // reset it
}
/**
@@ -1014,7 +1000,7 @@ final class ActivityStack {
* the resumed state (either by launching it or explicitly telling it),
* this function updates the rest of our state to match that fact.
*/
- private final void completeResumeLocked(ActivityRecord next) {
+ private void completeResumeLocked(ActivityRecord next) {
next.idle = false;
next.results = null;
next.newIntents = null;
@@ -1049,10 +1035,8 @@ final class ActivityStack {
final boolean ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
boolean forceHomeShown) {
ActivityRecord r = topRunningActivityLocked(null);
- if (r != null) {
- return ensureActivitiesVisibleLocked(r, starting, null, configChanges, forceHomeShown);
- }
- return false;
+ return r != null &&
+ ensureActivitiesVisibleLocked(r, starting, null, configChanges, forceHomeShown);
}
/**
@@ -1071,8 +1055,7 @@ final class ActivityStack {
boolean showHomeBehindStack = false;
boolean behindFullscreen = !mStackSupervisor.isFrontStack(this) &&
!(forceHomeShown && isHomeStack());
- int taskNdx;
- for (taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
@@ -1235,10 +1218,31 @@ final class ActivityStack {
return false;
}
- if (prev != null && prev.mLaunchHomeTaskNext && prev.finishing && topTask() == prev.task &&
- prev.task.getTopActivity() == null) {
+ final TaskRecord nextTask = next.task;
+ final TaskRecord prevTask = prev != null ? prev.task : null;
+ if (prevTask != null && prev.mLaunchHomeTaskNext && prev.finishing && prev.frontOfTask) {
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return mStackSupervisor.resumeHomeActivity(prev);
+ if (prevTask == nextTask) {
+ ArrayList<ActivityRecord> activities = prevTask.mActivities;
+ final int numActivities = activities.size();
+ for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ // r is usually the same as next, but what if two activities were launched
+ // before prev finished?
+ if (!r.finishing) {
+ r.mLaunchHomeTaskNext = true;
+ r.frontOfTask = true;
+ break;
+ }
+ }
+ } else if (prevTask != topTask()) {
+ // This task is going away but it was supposed to return to the home task.
+ // Now the task above it has to return to the home task instead.
+ final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
+ mTaskHistory.get(taskNdx).mActivities.get(0).mLaunchHomeTaskNext = true;
+ } else {
+ return mStackSupervisor.resumeHomeActivity(prev);
+ }
}
// If we are sleeping, and there is no resumed activity, and the top
@@ -1394,13 +1398,13 @@ final class ActivityStack {
// We are starting up the next activity, so tell the window manager
// that the previous one will be hidden soon. This way it can know
// to ignore it when computing the desired screen orientation.
- boolean noAnim = false;
+ boolean anim = true;
if (prev != null) {
if (prev.finishing) {
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare close transition: prev=" + prev);
if (mNoAnimActivities.contains(prev)) {
- noAnim = true;
+ anim = false;
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
} else {
mWindowManager.prepareAppTransition(prev.task == next.task
@@ -1412,7 +1416,7 @@ final class ActivityStack {
} else {
if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: prev=" + prev);
if (mNoAnimActivities.contains(next)) {
- noAnim = true;
+ anim = false;
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
} else {
mWindowManager.prepareAppTransition(prev.task == next.task
@@ -1427,13 +1431,13 @@ final class ActivityStack {
} else {
if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: no previous");
if (mNoAnimActivities.contains(next)) {
- noAnim = true;
+ anim = false;
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
} else {
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_OPEN, false);
}
}
- if (!noAnim) {
+ if (anim) {
next.applyOptionsLocked();
} else {
next.clearOptionsLocked();
@@ -1465,7 +1469,7 @@ final class ActivityStack {
// Have the window manager re-evaluate the orientation of
// the screen based on the new activity order.
- boolean updated = false;
+ boolean notUpdated = true;
if (mStackSupervisor.isFrontStack(this)) {
Configuration config = mWindowManager.updateOrientationFromAppTokens(
mService.mConfiguration,
@@ -1473,10 +1477,10 @@ final class ActivityStack {
if (config != null) {
next.frozenBeforeDestroy = true;
}
- updated = mService.updateConfigurationLocked(config, next, false, false);
+ notUpdated = !mService.updateConfigurationLocked(config, next, false, false);
}
- if (!updated) {
+ if (notUpdated) {
// The configuration update wasn't able to keep the existing
// instance of the activity, and instead started a new one.
// We should be all done, but let's just make sure our activity
@@ -1488,7 +1492,7 @@ final class ActivityStack {
+ ", new next: " + nextNext);
if (nextNext != next) {
// Do over!
- mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
+ mStackSupervisor.scheduleResumeTopActivities();
}
if (mStackSupervisor.reportResumedActivityLocked(next)) {
mNoAnimActivities.clear();
@@ -1594,7 +1598,6 @@ final class ActivityStack {
final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume, boolean keepCurTransition, Bundle options) {
- TaskRecord task = null;
TaskRecord rTask = r.task;
final int taskId = rTask.taskId;
if (taskForIdLocked(taskId) == null || newTask) {
@@ -1606,6 +1609,7 @@ final class ActivityStack {
mTaskHistory.add(rTask);
mWindowManager.moveTaskToTop(taskId);
}
+ TaskRecord task = null;
if (!newTask) {
// If starting in an existing task, find where that is...
boolean startIt = true;
@@ -1838,9 +1842,7 @@ final class ActivityStack {
final int targetTaskId = targetTask.taskId;
mWindowManager.setAppGroupId(target.appToken, targetTaskId);
- ThumbnailHolder curThumbHolder = target.thumbHolder;
- boolean gotOptions = !canMoveOptions;
-
+ boolean noOptions = canMoveOptions;
final int start = replyChainEnd < 0 ? i : replyChainEnd;
for (int srcPos = start; srcPos >= i; --srcPos) {
p = activities.get(srcPos);
@@ -1848,12 +1850,12 @@ final class ActivityStack {
continue;
}
- curThumbHolder = p.thumbHolder;
+ ThumbnailHolder curThumbHolder = p.thumbHolder;
canMoveOptions = false;
- if (!gotOptions && topOptions == null) {
+ if (noOptions && topOptions == null) {
topOptions = p.takeOptionsLocked();
if (topOptions != null) {
- gotOptions = true;
+ noOptions = false;
}
}
if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing activity " + p + " from task="
@@ -1889,18 +1891,17 @@ final class ActivityStack {
} else {
end = replyChainEnd;
}
- ActivityRecord p = null;
- boolean gotOptions = !canMoveOptions;
+ boolean noOptions = canMoveOptions;
for (int srcPos = i; srcPos <= end; srcPos++) {
- p = activities.get(srcPos);
+ ActivityRecord p = activities.get(srcPos);
if (p.finishing) {
continue;
}
canMoveOptions = false;
- if (!gotOptions && topOptions == null) {
+ if (noOptions && topOptions == null) {
topOptions = p.takeOptionsLocked();
if (topOptions != null) {
- gotOptions = true;
+ noOptions = false;
}
}
if (DEBUG_TASKS) Slog.w(TAG,
@@ -1930,7 +1931,7 @@ final class ActivityStack {
* @param topTaskIsHigher True if #task has already been processed by resetTaskIfNeededLocked.
* @param forceReset Flag passed in to resetTaskIfNeededLocked.
*/
- private final int resetAffinityTaskIfNeededLocked(TaskRecord affinityTask, TaskRecord task,
+ private int resetAffinityTaskIfNeededLocked(TaskRecord affinityTask, TaskRecord task,
boolean topTaskIsHigher, boolean forceReset, int taskInsertionPoint) {
int replyChainEnd = -1;
final int taskId = task.taskId;
@@ -2012,7 +2013,6 @@ final class ActivityStack {
// below so it remains singleTop.
if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
ArrayList<ActivityRecord> taskActivities = task.mActivities;
- boolean found = false;
int targetNdx = taskActivities.indexOf(target);
if (targetNdx > 0) {
ActivityRecord p = taskActivities.get(targetNdx - 1);
@@ -2083,25 +2083,6 @@ final class ActivityStack {
return taskTop;
}
- /**
- * Find the activity in the history stack within the given task. Returns
- * the index within the history at which it's found, or < 0 if not found.
- */
- final ActivityRecord findActivityInHistoryLocked(ActivityRecord r, TaskRecord task) {
- final ComponentName realActivity = r.realActivity;
- ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord candidate = activities.get(activityNdx);
- if (candidate.finishing) {
- continue;
- }
- if (candidate.realActivity.equals(realActivity)) {
- return candidate;
- }
- }
- return null;
- }
-
void sendActivityResultLocked(int callingUid, ActivityRecord r,
String resultWho, int requestCode, int resultCode, Intent data) {
@@ -2186,24 +2167,11 @@ final class ActivityStack {
}
}
- final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
- Configuration config) {
+ final ActivityRecord activityIdleInternalLocked(final IBinder token) {
if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
- ActivityRecord res = null;
-
- ArrayList<ActivityRecord> stops = null;
- ArrayList<ActivityRecord> finishes = null;
- ArrayList<UserStartedState> startingUsers = null;
- int NS = 0;
- int NF = 0;
- IApplicationThread sendThumbnail = null;
- boolean booting = false;
- boolean enableScreen = false;
- boolean activityRemoved = false;
-
// Get the activity record.
- res = isInStackLocked(token);
+ ActivityRecord res = isInStackLocked(token);
if (res != null) {
// No longer need to keep the device awake.
if (mResumedActivity == res && mLaunchingActivity.isHeld()) {
@@ -2213,7 +2181,7 @@ final class ActivityStack {
// If this activity is fullscreen, set up to hide those under it.
if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + res);
- ensureActivitiesVisibleLocked(null, 0);
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
}
return res;
@@ -2458,7 +2426,7 @@ final class ActivityStack {
boolean activityRemoved = destroyActivityLocked(r, true,
oomAdj, "finish-imm");
if (activityRemoved) {
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
}
return activityRemoved ? null : r;
}
@@ -2507,6 +2475,7 @@ final class ActivityStack {
resumeOK = controller.activityResuming(next.packageName);
} catch (RemoteException e) {
mService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
}
if (!resumeOK) {
@@ -2700,7 +2669,8 @@ final class ActivityStack {
}
}
if (activityRemoved) {
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
+
}
}
@@ -2817,7 +2787,7 @@ final class ActivityStack {
removeActivityFromHistoryLocked(r);
}
}
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -2948,7 +2918,7 @@ final class ActivityStack {
}
if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
// Caller wants the home activity moved with it. To accomplish this,
- // we'll just move the home task to the top first.
+ // we'll just indicate that this task returns to the home task.
task.mActivities.get(0).mLaunchHomeTaskNext = true;
}
moveTaskToFrontLocked(task, null, options);
@@ -2993,7 +2963,7 @@ final class ActivityStack {
mWindowManager.moveTaskToTop(tr.taskId);
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
if (VALIDATE_TOKENS) {
@@ -3030,6 +3000,7 @@ final class ActivityStack {
moveOK = mService.mController.activityResuming(next.packageName);
} catch (RemoteException e) {
mService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
}
if (!moveOK) {
return false;
@@ -3066,11 +3037,12 @@ final class ActivityStack {
if (mResumedActivity != null && mResumedActivity.task == tr &&
mResumedActivity.mLaunchHomeTaskNext) {
+ // TODO: Can we skip the next line and just pass mResumedAct. to resumeHomeAct.()?
mResumedActivity.mLaunchHomeTaskNext = false;
return mStackSupervisor.resumeHomeActivity(null);
}
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
return true;
}
@@ -3418,14 +3390,14 @@ final class ActivityStack {
ActivityStack stack = mStackSupervisor.getFocusedStack();
if (stack == null) {
mStackSupervisor.resumeHomeActivity(null);
- } else if (!stack.resumeTopActivityLocked(null)) {
+ } else if (!mStackSupervisor.resumeTopActivitiesLocked(stack, null, null)) {
// If there was nothing to resume, and we are not already
// restarting this process, but there is a visible activity that
// is hosted by the process... then make sure all visible
// activities are running, taking care of restarting this
// process.
if (hasVisibleActivities) {
- ensureActivitiesVisibleLocked(null, 0);
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
}
}
}
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 22d7780..f7f0812 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -26,6 +26,7 @@ import static com.android.server.am.ActivityManagerService.DEBUG_RESULTS;
import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH;
import static com.android.server.am.ActivityManagerService.DEBUG_TASKS;
import static com.android.server.am.ActivityManagerService.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.am.ActivityManagerService.TAG;
import android.app.Activity;
@@ -51,6 +52,7 @@ import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -83,14 +85,16 @@ public class ActivityStackSupervisor {
static final boolean DEBUG_APP = DEBUG || false;
static final boolean DEBUG_SAVED_STATE = DEBUG || false;
static final boolean DEBUG_STATES = DEBUG || false;
+ static final boolean DEBUG_IDLE = DEBUG || false;
public static final int HOME_STACK_ID = 0;
/** How long we wait until giving up on the last activity telling us it is idle. */
static final int IDLE_TIMEOUT = 10*1000;
- static final int IDLE_TIMEOUT_MSG = ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
- static final int IDLE_NOW_MSG = ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG + 1;
+ static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG;
+ static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1;
+ static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
final ActivityManagerService mService;
final Context mContext;
@@ -236,7 +240,7 @@ public class ActivityStackSupervisor {
prev.mLaunchHomeTaskNext = false;
}
if (mHomeStack.topRunningActivityLocked(null) != null) {
- return mHomeStack.resumeTopActivityLocked(prev);
+ return resumeTopActivitiesLocked(mHomeStack, prev, null);
}
return mService.startHomeActivityLocked(mCurrentUser);
}
@@ -345,11 +349,12 @@ public class ActivityStackSupervisor {
+ hr.intent.getComponent().flattenToShortString(), e);
throw e;
}
- } else {
- stack.ensureActivitiesVisibleLocked(hr, null, processName, 0, false);
}
}
}
+ if (!didSomething) {
+ ensureActivitiesVisibleLocked(null, 0);
+ }
return didSomething;
}
@@ -1249,7 +1254,7 @@ public class ActivityStackSupervisor {
}
final ActivityStack sourceStack;
- final TaskRecord sourceTask;
+ TaskRecord sourceTask;
if (sourceRecord != null) {
sourceTask = sourceRecord.task;
sourceStack = sourceTask.stack;
@@ -1341,7 +1346,7 @@ public class ActivityStackSupervisor {
// sure we have correctly resumed the top activity.
if (doResume) {
setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
- targetStack.resumeTopActivityLocked(null, options);
+ resumeTopActivitiesLocked(targetStack, null, options);
} else {
ActivityOptions.abort(options);
}
@@ -1474,7 +1479,7 @@ public class ActivityStackSupervisor {
// resumed the top activity.
if (doResume) {
setLaunchHomeTaskNextFlag(sourceRecord, null, topStack);
- topStack.resumeTopActivityLocked(null);
+ resumeTopActivitiesLocked();
}
ActivityOptions.abort(options);
if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1535,14 +1540,15 @@ public class ActivityStackSupervisor {
}
}
} else if (sourceRecord != null) {
- targetStack = sourceRecord.task.stack;
+ sourceTask = sourceRecord.task;
+ targetStack = sourceTask.stack;
moveHomeStack(targetStack.isHomeStack());
if (!addingToTask &&
(launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
// 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.
- ActivityRecord top = sourceRecord.task.performClearTaskLocked(r, launchFlags);
+ ActivityRecord top = sourceTask.performClearTaskLocked(r, launchFlags);
keepCurTransition = true;
if (top != null) {
ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
@@ -1564,8 +1570,7 @@ public class ActivityStackSupervisor {
// In this case, we are launching an activity in our own task
// that may already be running somewhere in the history, and
// we want to shuffle it to the front of the stack if so.
- final ActivityRecord top =
- targetStack.findActivityInHistoryLocked(r, sourceRecord.task);
+ final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r);
if (top != null) {
final TaskRecord task = top.task;
task.moveActivityToFrontLocked(top);
@@ -1585,7 +1590,7 @@ public class ActivityStackSupervisor {
// An existing activity is starting this new activity, so we want
// to keep the new one in the same task as the one that is starting
// it.
- r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
+ r.setTask(sourceTask, sourceRecord.thumbHolder, false);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in existing task " + r.task);
@@ -1635,9 +1640,11 @@ public class ActivityStackSupervisor {
ActivityRecord r = ActivityRecord.forToken(token);
if (r != null) {
+ if (DEBUG_IDLE) Slog.d(TAG, "activityIdleInternalLocked: Callers=" +
+ Debug.getCallers(4));
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
r.finishLaunchTickingLocked();
- res = r.task.stack.activityIdleInternalLocked(token, fromTimeout, config);
+ res = r.task.stack.activityIdleInternalLocked(token);
if (res != null) {
if (fromTimeout) {
reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
@@ -1758,7 +1765,7 @@ public class ActivityStackSupervisor {
}
if (activityRemoved) {
- getFocusedStack().resumeTopActivityLocked(null);
+ resumeTopActivitiesLocked();
}
return res;
@@ -1795,13 +1802,27 @@ public class ActivityStackSupervisor {
return didSomething;
}
- void resumeTopActivitiesLocked() {
+ boolean resumeTopActivitiesLocked() {
+ return resumeTopActivitiesLocked(null, null, null);
+ }
+
+ boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
+ Bundle targetOptions) {
+ if (targetStack == null) {
+ targetStack = getFocusedStack();
+ }
+ boolean result = false;
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
if (isFrontStack(stack)) {
- stack.resumeTopActivityLocked(null);
+ if (stack == targetStack) {
+ result = stack.resumeTopActivityLocked(target, targetOptions);
+ } else {
+ stack.resumeTopActivityLocked(null);
+ }
}
}
+ return result;
}
void finishTopRunningActivityLocked(ProcessRecord app) {
@@ -1854,7 +1875,7 @@ public class ActivityStackSupervisor {
return;
}
stack.moveTask(taskId, toTop);
- stack.resumeTopActivityLocked(null);
+ resumeTopActivitiesLocked();
}
ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
@@ -1913,7 +1934,7 @@ public class ActivityStackSupervisor {
final ActivityStack stack = mStacks.get(stackNdx);
stack.awakeFromSleepingLocked();
if (isFrontStack(stack)) {
- stack.resumeTopActivityLocked(null);
+ resumeTopActivitiesLocked();
}
}
}
@@ -1980,7 +2001,7 @@ public class ActivityStackSupervisor {
}
mStartingUsers.add(uss);
- boolean haveActivities = mHomeStack.switchUserLocked(userId, uss);
+ boolean haveActivities = mHomeStack.switchUserLocked(userId);
resumeTopActivitiesLocked();
@@ -2195,18 +2216,24 @@ public class ActivityStackSupervisor {
}
void scheduleIdleTimeoutLocked(ActivityRecord next) {
+ if (DEBUG_IDLE) Slog.d(TAG, "scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
}
final void scheduleIdleLocked() {
- mHandler.obtainMessage(IDLE_NOW_MSG).sendToTarget();
+ mHandler.sendEmptyMessage(IDLE_NOW_MSG);
}
void removeTimeoutsForActivityLocked(ActivityRecord r) {
+ if (DEBUG_IDLE) Slog.d(TAG, "removeTimeoutsForActivity: Callers=" + Debug.getCallers(4));
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
}
+ final void scheduleResumeTopActivities() {
+ mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
+ }
+
private final class ActivityStackSupervisorHandler extends Handler {
public ActivityStackSupervisorHandler(Looper looper) {
@@ -2223,6 +2250,8 @@ public class ActivityStackSupervisor {
public void handleMessage(Message msg) {
switch (msg.what) {
case IDLE_TIMEOUT_MSG: {
+ if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_TIMEOUT_MSG: Callers=" +
+ Debug.getCallers(4));
if (mService.mDidDexOpt) {
mService.mDidDexOpt = false;
Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
@@ -2237,6 +2266,11 @@ public class ActivityStackSupervisor {
case IDLE_NOW_MSG: {
activityIdleInternal((ActivityRecord)msg.obj);
} break;
+ case RESUME_TOP_ACTIVITY_MSG: {
+ synchronized (mService) {
+ resumeTopActivitiesLocked();
+ }
+ } break;
}
}
}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index fccaab5..9fdd293 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -371,15 +371,15 @@ class ServiceRecord extends Binder {
return;
}
try {
- if (foregroundNoti.icon == 0) {
+ if (localForegroundNoti.icon == 0) {
// It is not correct for the caller to supply a notification
// icon, but this used to be able to slip through, so for
// those dirty apps give it the app's icon.
- foregroundNoti.icon = appInfo.icon;
+ localForegroundNoti.icon = appInfo.icon;
// Do not allow apps to present a sneaky invisible content view either.
- foregroundNoti.contentView = null;
- foregroundNoti.bigContentView = null;
+ localForegroundNoti.contentView = null;
+ localForegroundNoti.bigContentView = null;
CharSequence appName = appInfo.loadLabel(
ams.mContext.getPackageManager());
if (appName == null) {
@@ -395,7 +395,7 @@ class ServiceRecord extends Binder {
appInfo.packageName, null));
PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- foregroundNoti.setLatestEventInfo(ctx,
+ localForegroundNoti.setLatestEventInfo(ctx,
ams.mContext.getString(
com.android.internal.R.string
.app_running_notification_title,
@@ -406,10 +406,10 @@ class ServiceRecord extends Binder {
appName),
pi);
} catch (PackageManager.NameNotFoundException e) {
- foregroundNoti.icon = 0;
+ localForegroundNoti.icon = 0;
}
}
- if (foregroundNoti.icon == 0) {
+ if (localForegroundNoti.icon == 0) {
// Notifications whose icon is 0 are defined to not show
// a notification, silently ignoring it. We don't want to
// just ignore it, we want to prevent the service from
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index dda82ec..a01b882 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -383,6 +383,24 @@ class TaskRecord extends ThumbnailHolder {
return thumbs;
}
+ /**
+ * Find the activity in the history stack within the given task. Returns
+ * the index within the history at which it's found, or < 0 if not found.
+ */
+ final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
+ final ComponentName realActivity = r.realActivity;
+ for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord candidate = mActivities.get(activityNdx);
+ if (candidate.finishing) {
+ continue;
+ }
+ if (candidate.realActivity.equals(realActivity)) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+
void dump(PrintWriter pw, String prefix) {
if (numActivities != 0 || rootWasReset || userId != 0) {
pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 32f39b7..87263b3 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -37,13 +37,10 @@ import android.net.NetworkInfo;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.os.Binder;
-import android.os.HandlerThread;
-import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -53,6 +50,7 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.IState;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.server.IoThread;
import com.google.android.collect.Lists;
import java.io.FileDescriptor;
@@ -100,7 +98,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private final INetworkStatsService mStatsService;
private final IConnectivityManager mConnService;
private Looper mLooper;
- private HandlerThread mThread;
private HashMap<String, TetherInterfaceSM> mIfaces; // all tethered/tetherable ifaces
@@ -147,9 +144,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mIfaces = new HashMap<String, TetherInterfaceSM>();
// make our own thread so we don't anr the system
- mThread = new HandlerThread("Tethering");
- mThread.start();
- mLooper = mThread.getLooper();
+ mLooper = IoThread.get().getLooper();
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
mTetherMasterSM.start();
diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java
index 1c883ec..cf593ce 100644
--- a/services/java/com/android/server/content/SyncManager.java
+++ b/services/java/com/android/server/content/SyncManager.java
@@ -53,12 +53,10 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
-import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -74,6 +72,7 @@ import android.util.Pair;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.accounts.AccountManagerService;
import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
@@ -82,7 +81,6 @@ import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
import java.io.FileDescriptor;
-import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -381,10 +379,7 @@ public class SyncManager {
mSyncAdapters = new SyncAdaptersCache(mContext);
mSyncQueue = new SyncQueue(mContext.getPackageManager(), mSyncStorageEngine, mSyncAdapters);
- HandlerThread syncThread = new HandlerThread("SyncHandlerThread",
- Process.THREAD_PRIORITY_BACKGROUND);
- syncThread.start();
- mSyncHandler = new SyncHandler(syncThread.getLooper());
+ mSyncHandler = new SyncHandler(BackgroundThread.get().getLooper());
mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
@Override
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 17b0662..ca85e42 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -37,6 +37,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
+import com.android.server.UiThread;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -190,12 +191,12 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
private final DisplayViewport mTempDefaultViewport = new DisplayViewport();
private final DisplayViewport mTempExternalTouchViewport = new DisplayViewport();
- public DisplayManagerService(Context context, Handler mainHandler, Handler uiHandler) {
+ public DisplayManagerService(Context context, Handler mainHandler) {
mContext = context;
mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
mHandler = new DisplayManagerHandler(mainHandler.getLooper());
- mUiHandler = uiHandler;
+ mUiHandler = UiThread.getHandler();
mDisplayAdapterListener = new DisplayAdapterListener();
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index a82f421..5ca7242 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -106,7 +106,6 @@ import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.INetworkManagementService;
import android.os.IPowerManager;
import android.os.Message;
@@ -134,6 +133,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Objects;
+import com.android.server.IoThread;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
@@ -274,7 +274,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
INetworkPolicyListener>();
- private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final AtomicFile mPolicyFile;
@@ -306,9 +305,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mNetworkManager = checkNotNull(networkManagement, "missing networkManagement");
mTime = checkNotNull(time, "missing TrustedTime");
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
+ mHandler = new Handler(IoThread.get().getLooper(), mHandlerCallback);
mSuppressDefaultPolicy = suppressDefaultPolicy;
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 74be472..5074409 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -96,7 +96,6 @@ import android.os.Binder;
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.INetworkManagementService;
import android.os.Message;
import android.os.PowerManager;
@@ -120,6 +119,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
+import com.android.server.IoThread;
import com.android.server.connectivity.Tethering;
import com.google.android.collect.Maps;
@@ -240,7 +240,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
/** Data layer operation counters for splicing into other structures. */
private NetworkStats mUidOperations = new NetworkStats(0L, 10);
- private final HandlerThread mHandlerThread;
private final Handler mHandler;
private boolean mSystemReady;
@@ -271,9 +270,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
+ mHandler = new Handler(IoThread.get().getLooper(), mHandlerCallback);
mSystemDir = checkNotNull(systemDir);
mBaseDir = new File(systemDir, "netstats");
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index d9c85bf..734d071 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -307,8 +307,8 @@ public final class Installer {
return execute(builder.toString());
}
- public int getSizeInfo(String pkgName, int persona, String apkPath, String fwdLockApkPath,
- String asecPath, PackageStats pStats) {
+ public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
+ String fwdLockApkPath, String asecPath, PackageStats pStats) {
StringBuilder builder = new StringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
@@ -317,6 +317,8 @@ public final class Installer {
builder.append(' ');
builder.append(apkPath);
builder.append(' ');
+ builder.append(libDirPath != null ? libDirPath : "!");
+ builder.append(' ');
builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
builder.append(' ');
builder.append(asecPath != null ? asecPath : "!");
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 3824022..47282ba 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -425,8 +425,71 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package mPlatformPackage;
// Set of pending broadcasts for aggregating enable/disable of components.
- final HashMap<String, ArrayList<String>> mPendingBroadcasts
- = new HashMap<String, ArrayList<String>>();
+ static class PendingPackageBroadcasts {
+ // for each user id, a map of <package name -> components within that package>
+ final SparseArray<HashMap<String, ArrayList<String>>> mUidMap;
+
+ public PendingPackageBroadcasts() {
+ mUidMap = new SparseArray<HashMap<String, ArrayList<String>>>();
+ }
+
+ public ArrayList<String> get(int userId, String packageName) {
+ HashMap<String, ArrayList<String>> packages = getOrAllocate(userId);
+ return packages.get(packageName);
+ }
+
+ public void put(int userId, String packageName, ArrayList<String> components) {
+ HashMap<String, ArrayList<String>> packages = getOrAllocate(userId);
+ packages.put(packageName, components);
+ }
+
+ public void remove(int userId, String packageName) {
+ HashMap<String, ArrayList<String>> packages = mUidMap.get(userId);
+ if (packages != null) {
+ packages.remove(packageName);
+ }
+ }
+
+ public void remove(int userId) {
+ mUidMap.remove(userId);
+ }
+
+ public int userIdCount() {
+ return mUidMap.size();
+ }
+
+ public int userIdAt(int n) {
+ return mUidMap.keyAt(n);
+ }
+
+ public HashMap<String, ArrayList<String>> packagesForUserId(int userId) {
+ return mUidMap.get(userId);
+ }
+
+ public int size() {
+ // total number of pending broadcast entries across all userIds
+ int num = 0;
+ for (int i = 0; i< mUidMap.size(); i++) {
+ num += mUidMap.valueAt(i).size();
+ }
+ return num;
+ }
+
+ public void clear() {
+ mUidMap.clear();
+ }
+
+ private HashMap<String, ArrayList<String>> getOrAllocate(int userId) {
+ HashMap<String, ArrayList<String>> map = mUidMap.get(userId);
+ if (map == null) {
+ map = new HashMap<String, ArrayList<String>>();
+ mUidMap.put(userId, map);
+ }
+ return map;
+ }
+ }
+ final PendingPackageBroadcasts mPendingBroadcasts = new PendingPackageBroadcasts();
+
// Service Connection to remote media container service to copy
// package uri's from external media onto secure containers
// or internal storage.
@@ -670,16 +733,23 @@ public class PackageManagerService extends IPackageManager.Stub {
packages = new String[size];
components = new ArrayList[size];
uids = new int[size];
- Iterator<Map.Entry<String, ArrayList<String>>>
- it = mPendingBroadcasts.entrySet().iterator();
- int i = 0;
- while (it.hasNext() && i < size) {
- Map.Entry<String, ArrayList<String>> ent = it.next();
- packages[i] = ent.getKey();
- components[i] = ent.getValue();
- PackageSetting ps = mSettings.mPackages.get(ent.getKey());
- uids[i] = (ps != null) ? ps.appId : -1;
- i++;
+ int i = 0; // filling out the above arrays
+
+ for (int n = 0; n < mPendingBroadcasts.userIdCount(); n++) {
+ int packageUserId = mPendingBroadcasts.userIdAt(n);
+ Iterator<Map.Entry<String, ArrayList<String>>> it
+ = mPendingBroadcasts.packagesForUserId(packageUserId)
+ .entrySet().iterator();
+ while (it.hasNext() && i < size) {
+ Map.Entry<String, ArrayList<String>> ent = it.next();
+ packages[i] = ent.getKey();
+ components[i] = ent.getValue();
+ PackageSetting ps = mSettings.mPackages.get(ent.getKey());
+ uids[i] = (ps != null)
+ ? UserHandle.getUid(packageUserId, ps.appId)
+ : -1;
+ i++;
+ }
}
size = i;
mPendingBroadcasts.clear();
@@ -9191,18 +9261,22 @@ public class PackageManagerService extends IPackageManager.Stub {
}
PackageParser.Package p;
boolean dataOnly = false;
+ String libDirPath = null;
String asecPath = null;
synchronized (mPackages) {
p = mPackages.get(packageName);
+ PackageSetting ps = mSettings.mPackages.get(packageName);
if(p == null) {
dataOnly = true;
- PackageSetting ps = mSettings.mPackages.get(packageName);
if((ps == null) || (ps.pkg == null)) {
Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
return false;
}
p = ps.pkg;
}
+ if (ps != null) {
+ libDirPath = ps.nativeLibraryPathString;
+ }
if (p != null && (isExternal(p) || isForwardLocked(p))) {
String secureContainerId = cidFromCodePath(p.applicationInfo.sourceDir);
if (secureContainerId != null) {
@@ -9221,8 +9295,8 @@ public class PackageManagerService extends IPackageManager.Stub {
publicSrcDir = applicationInfo.publicSourceDir;
}
}
- int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, publicSrcDir,
- asecPath, pStats);
+ int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, libDirPath,
+ publicSrcDir, asecPath, pStats);
if (res < 0) {
return false;
}
@@ -9568,8 +9642,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
mSettings.writePackageRestrictionsLPr(userId);
- packageUid = UserHandle.getUid(userId, pkgSetting.appId);
- components = mPendingBroadcasts.get(packageName);
+ components = mPendingBroadcasts.get(userId, packageName);
final boolean newPackage = components == null;
if (newPackage) {
components = new ArrayList<String>();
@@ -9581,10 +9654,10 @@ public class PackageManagerService extends IPackageManager.Stub {
sendNow = true;
// Purge entry from pending broadcast list if another one exists already
// since we are sending one right away.
- mPendingBroadcasts.remove(packageName);
+ mPendingBroadcasts.remove(userId, packageName);
} else {
if (newPackage) {
- mPendingBroadcasts.put(packageName, components);
+ mPendingBroadcasts.put(userId, packageName, components);
}
if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
// Schedule a message
@@ -9596,6 +9669,7 @@ public class PackageManagerService extends IPackageManager.Stub {
long callingId = Binder.clearCallingIdentity();
try {
if (sendNow) {
+ packageUid = UserHandle.getUid(userId, pkgSetting.appId);
sendPackageChangedBroadcast(packageName,
(flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
}
@@ -10743,8 +10817,9 @@ public class PackageManagerService extends IPackageManager.Stub {
/** Called by UserManagerService */
void cleanUpUserLILPw(int userHandle) {
- if (mDirtyUsers.remove(userHandle));
+ mDirtyUsers.remove(userHandle);
mSettings.removeUserLPr(userHandle);
+ mPendingBroadcasts.remove(userHandle);
if (mInstaller != null) {
// Technically, we shouldn't be doing this with the package lock
// held. However, this is very rare, and there is already so much
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 1203e02..198851c 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -50,6 +50,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.SystemService;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -364,8 +365,6 @@ public final class PowerManagerService extends IPowerManager.Stub
private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE;
private native void nativeInit();
- private static native void nativeShutdown();
- private static native void nativeReboot(String reason) throws IOException;
private static native void nativeSetPowerState(boolean screenOn, boolean screenBright);
private static native void nativeAcquireSuspendBlocker(String name);
@@ -2164,18 +2163,26 @@ public final class PowerManagerService extends IPowerManager.Stub
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
*/
public static void lowLevelShutdown() {
- nativeShutdown();
+ SystemProperties.set("sys.powerctl", "shutdown");
}
/**
- * Low-level function to reboot the device.
+ * Low-level function to reboot the device. On success, this function
+ * doesn't return. If more than 5 seconds passes from the time,
+ * a reboot is requested, this method returns.
*
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
- * @throws IOException if reboot fails for some reason (eg, lack of
- * permission)
*/
- public static void lowLevelReboot(String reason) throws IOException {
- nativeReboot(reason);
+ public static void lowLevelReboot(String reason) {
+ if (reason == null) {
+ reason = "";
+ }
+ SystemProperties.set("sys.powerctl", "reboot," + reason);
+ try {
+ Thread.sleep(20000);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
}
@Override // Watchdog.Monitor implementation
diff --git a/services/java/com/android/server/power/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java
index c7f7390..ba321bc 100644
--- a/services/java/com/android/server/power/ShutdownThread.java
+++ b/services/java/com/android/server/power/ShutdownThread.java
@@ -490,11 +490,8 @@ public final class ShutdownThread extends Thread {
public static void rebootOrShutdown(boolean reboot, String reason) {
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
- try {
- PowerManagerService.lowLevelReboot(reason);
- } catch (Exception e) {
- Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
- }
+ PowerManagerService.lowLevelReboot(reason);
+ Log.e(TAG, "Reboot failed, will attempt shutdown instead");
} else if (SHUTDOWN_VIBRATE_MS > 0) {
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator();
diff --git a/services/java/com/android/server/usb/UsbDebuggingManager.java b/services/java/com/android/server/usb/UsbDebuggingManager.java
index 93d3114..ba3f1d1 100644
--- a/services/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/java/com/android/server/usb/UsbDebuggingManager.java
@@ -22,15 +22,14 @@ import android.content.Intent;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Looper;
import android.os.Message;
-import android.os.Process;
import android.os.SystemClock;
import android.util.Slog;
import android.util.Base64;
+import com.android.server.FgThread;
import java.lang.Thread;
import java.io.File;
@@ -54,7 +53,6 @@ public class UsbDebuggingManager implements Runnable {
private final Context mContext;
private final Handler mHandler;
- private final HandlerThread mHandlerThread;
private Thread mThread;
private boolean mAdbEnabled = false;
private String mFingerprints;
@@ -62,9 +60,7 @@ public class UsbDebuggingManager implements Runnable {
private OutputStream mOutputStream = null;
public UsbDebuggingManager(Context context) {
- mHandlerThread = new HandlerThread("UsbDebuggingHandler");
- mHandlerThread.start();
- mHandler = new UsbDebuggingHandler(mHandlerThread.getLooper());
+ mHandler = new UsbDebuggingHandler(FgThread.get().getLooper());
mContext = context;
}
@@ -165,7 +161,7 @@ public class UsbDebuggingManager implements Runnable {
mAdbEnabled = true;
- mThread = new Thread(UsbDebuggingManager.this);
+ mThread = new Thread(UsbDebuggingManager.this, "UsbDebuggingManager");
mThread.start();
break;
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 87aa8cce..3a5357a 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -32,11 +32,9 @@ import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
-import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UEventObserver;
@@ -48,6 +46,7 @@ import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.FgThread;
import java.io.File;
import java.io.FileDescriptor;
@@ -158,11 +157,7 @@ public class UsbDeviceManager {
readOemUsbOverrideConfig();
- // create a thread for our Handler
- HandlerThread thread = new HandlerThread("UsbDeviceManager",
- Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
- mHandler = new UsbHandler(thread.getLooper());
+ mHandler = new UsbHandler(FgThread.get().getLooper());
if (nativeIsStartRequested()) {
if (DEBUG) Slog.d(TAG, "accessory attached at boot");
diff --git a/services/java/com/android/server/wm/DimLayer.java b/services/java/com/android/server/wm/DimLayer.java
index 91d6995..7839d06 100644
--- a/services/java/com/android/server/wm/DimLayer.java
+++ b/services/java/com/android/server/wm/DimLayer.java
@@ -3,6 +3,7 @@
package com.android.server.wm;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.os.SystemClock;
import android.util.Slog;
import android.view.DisplayInfo;
@@ -26,8 +27,11 @@ public class DimLayer {
/** Last value passed to mDimSurface.setLayer() */
int mLayer = -1;
- /** Last values passed to mDimSurface.setSize() */
- int mLastDimWidth, mLastDimHeight;
+ /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
+ Rect mBounds = new Rect();
+
+ /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
+ Rect mLastBounds = new Rect();
/** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
private boolean mShowing = false;
@@ -116,6 +120,10 @@ public class DimLayer {
}
}
+ void setBounds(Rect bounds) {
+ mBounds.set(bounds);
+ }
+
/**
* @param duration The time to test.
* @return True if the duration would lead to an earlier end to the current animation.
@@ -151,6 +159,7 @@ public class DimLayer {
return;
}
+ /*
// Set surface size to screen size.
final DisplayInfo info = mDisplayContent.getDisplayInfo();
// Multiply by 1.5 so that rotating a frozen surface that includes this does not expose a
@@ -160,17 +169,17 @@ public class DimLayer {
// back off position so 1/4 of Surface is before and 1/4 is after.
final float xPos = -1 * dw / 6;
final float yPos = -1 * dh / 6;
+ */
- if (mLastDimWidth != dw || mLastDimHeight != dh || mLayer != layer) {
+ if (!mLastBounds.equals(mBounds) || mLayer != layer) {
try {
- mDimSurface.setPosition(xPos, yPos);
- mDimSurface.setSize(dw, dh);
+ mDimSurface.setPosition(mBounds.left, mBounds.top);
+ mDimSurface.setSize(mBounds.width(), mBounds.height());
mDimSurface.setLayer(layer);
} catch (RuntimeException e) {
Slog.w(TAG, "Failure setting size or layer", e);
}
- mLastDimWidth = dw;
- mLastDimHeight = dh;
+ mLastBounds.set(mBounds);
mLayer = layer;
}
@@ -257,8 +266,8 @@ public class DimLayer {
pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
pw.print(" mLayer="); pw.print(mLayer);
pw.print(" mAlpha="); pw.println(mAlpha);
- pw.print(prefix); pw.print("mLastDimWidth="); pw.print(mLastDimWidth);
- pw.print(" mLastDimHeight="); pw.println(mLastDimHeight);
+ pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
+ pw.print(" mBounds="); pw.println(mBounds.toShortString());
pw.print(prefix); pw.print("Last animation: ");
pw.print(" mDuration="); pw.print(mDuration);
pw.print(" mStartTime="); pw.print(mStartTime);
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index dd675db..af7db96 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -202,7 +202,8 @@ class DisplayContent {
}
/** Refer to {@link WindowManagerService#createStack(int, int, int, float)} */
- TaskStack createStack(int stackId, int relativeStackId, int position, float weight) {
+ TaskStack createStack(WindowManagerService service, int stackId, int relativeStackId,
+ int position, float weight) {
TaskStack newStack = null;
if (DEBUG_STACK) Slog.d(TAG, "createStack: stackId=" + stackId + " relativeStackId="
+ relativeStackId + " position=" + position + " weight=" + weight);
@@ -211,9 +212,9 @@ class DisplayContent {
throw new IllegalArgumentException("createStack: First stackId not "
+ HOME_STACK_ID);
}
- StackBox newBox = new StackBox(this, null);
+ StackBox newBox = new StackBox(service, this, null);
mStackBoxes.add(newBox);
- newStack = new TaskStack(stackId, this);
+ newStack = new TaskStack(service, stackId, this);
newStack.mStackBox = newBox;
newBox.mStack = newStack;
mHomeStack = newStack;
@@ -225,8 +226,8 @@ class DisplayContent {
|| position == StackBox.TASK_STACK_GOES_UNDER) {
// Position indicates a new box is added at top level only.
if (box.contains(relativeStackId)) {
- StackBox newBox = new StackBox(this, null);
- newStack = new TaskStack(stackId, this);
+ StackBox newBox = new StackBox(service, this, null);
+ newStack = new TaskStack(service, stackId, this);
newStack.mStackBox = newBox;
newBox.mStack = newStack;
final int offset = position == StackBox.TASK_STACK_GOES_OVER ? 1 : 0;
@@ -371,6 +372,40 @@ class DisplayContent {
}
}
+ void resetAnimationBackgroundAnimator() {
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ mStackBoxes.get(stackBoxNdx).resetAnimationBackgroundAnimator();
+ }
+ }
+
+ boolean animateDimLayers() {
+ boolean result = false;
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ result |= mStackBoxes.get(stackBoxNdx).animateDimLayers();
+ }
+ return result;
+ }
+
+ void resetDimming() {
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ mStackBoxes.get(stackBoxNdx).resetDimming();
+ }
+ }
+
+ boolean isDimming() {
+ boolean result = false;
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ result |= mStackBoxes.get(stackBoxNdx).isDimming();
+ }
+ return result;
+ }
+
+ void stopDimmingIfNeeded() {
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ mStackBoxes.get(stackBoxNdx).stopDimmingIfNeeded();
+ }
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
final String subPrefix = " " + prefix;
diff --git a/services/java/com/android/server/wm/StackBox.java b/services/java/com/android/server/wm/StackBox.java
index 3891aea..2b43b40 100644
--- a/services/java/com/android/server/wm/StackBox.java
+++ b/services/java/com/android/server/wm/StackBox.java
@@ -24,11 +24,10 @@ import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
import static com.android.server.wm.WindowManagerService.TAG;
import java.io.PrintWriter;
-import java.util.ArrayList;
public class StackBox {
/** Used with {@link WindowManagerService#createStack}. To left of, lower l/r Rect values. */
- public static final int TASK_STACK_GOES_BEFORE = 0; //
+ public static final int TASK_STACK_GOES_BEFORE = 0;
/** Used with {@link WindowManagerService#createStack}. To right of, higher l/r Rect values. */
public static final int TASK_STACK_GOES_AFTER = 1;
/** Used with {@link WindowManagerService#createStack}. Vertical: lower t/b Rect values. */
@@ -40,6 +39,9 @@ public class StackBox {
/** Used with {@link WindowManagerService#createStack}. Put on a lower layer on display. */
public static final int TASK_STACK_GOES_UNDER = 5;
+ /** The service */
+ final WindowManagerService mService;
+
/** The display this box sits in. */
final DisplayContent mDisplayContent;
@@ -68,13 +70,11 @@ public class StackBox {
/** Dirty flag. Something inside this or some descendant of this has changed. */
boolean layoutNeeded;
- /** Used to keep from reallocating a temporary array to hold the list of Tasks below */
- ArrayList<Task> mTmpTasks = new ArrayList<Task>();
-
/** Used to keep from reallocating a temporary Rect for propagating bounds to child boxes */
Rect mTmpRect = new Rect();
- StackBox(DisplayContent displayContent, StackBox parent) {
+ StackBox(WindowManagerService service, DisplayContent displayContent, StackBox parent) {
+ mService = service;
mDisplayContent = displayContent;
mParent = parent;
}
@@ -87,15 +87,6 @@ public class StackBox {
}
}
- /** Propagate #layoutNeeded top down. */
- void makeClean() {
- layoutNeeded = false;
- if (mFirst != null) {
- mFirst.makeClean();
- mSecond.makeClean();
- }
- }
-
/**
* Determine if a particular TaskStack is in this StackBox or any of its descendants.
* @param stackId The TaskStack being considered.
@@ -132,10 +123,7 @@ public class StackBox {
* @return true if this is the first child.
*/
boolean isFirstChild() {
- if (mParent == null) {
- return false;
- }
- return mParent.mFirst == this;
+ return mParent != null && mParent.mFirst == this;
}
/** Returns the bounds of the specified TaskStack if it is contained in this StackBox.
@@ -180,7 +168,7 @@ public class StackBox {
}
// Found it!
- TaskStack stack = new TaskStack(stackId, mDisplayContent);
+ TaskStack stack = new TaskStack(mService, stackId, mDisplayContent);
TaskStack firstStack;
TaskStack secondStack;
switch (position) {
@@ -213,11 +201,11 @@ public class StackBox {
break;
}
- mFirst = new StackBox(mDisplayContent, this);
+ mFirst = new StackBox(mService, mDisplayContent, this);
firstStack.mStackBox = mFirst;
mFirst.mStack = firstStack;
- mSecond = new StackBox(mDisplayContent, this);
+ mSecond = new StackBox(mService, mDisplayContent, this);
secondStack.mStackBox = mSecond;
mSecond.mStack = secondStack;
@@ -225,22 +213,6 @@ public class StackBox {
return stack;
}
- /**
- * @return List of all Tasks underneath this StackBox. The order is currently mFirst followed
- * by mSecond putting mSecond Tasks more recent than mFirst Tasks.
- * TODO: Change to MRU ordering.
- */
- ArrayList<Task> getTasks() {
- mTmpTasks.clear();
- if (mStack != null) {
- mTmpTasks.addAll(mStack.getTasks());
- } else {
- mTmpTasks.addAll(mFirst.getTasks());
- mTmpTasks.addAll(mSecond.getTasks());
- }
- return mTmpTasks;
- }
-
/** Return the stackId of the first mFirst StackBox with a non-null mStack */
int getStackId() {
if (mStack != null) {
@@ -303,6 +275,7 @@ public class StackBox {
if (mStack != null) {
change = !mBounds.equals(bounds);
mBounds.set(bounds);
+ mStack.setBounds(bounds);
} else {
mTmpRect.set(bounds);
if (mVertical) {
@@ -326,6 +299,51 @@ public class StackBox {
return change;
}
+ void resetAnimationBackgroundAnimator() {
+ if (mStack != null) {
+ mStack.resetAnimationBackgroundAnimator();
+ return;
+ }
+ mFirst.resetAnimationBackgroundAnimator();
+ mSecond.resetAnimationBackgroundAnimator();
+ }
+
+ boolean animateDimLayers() {
+ if (mStack != null) {
+ return mStack.animateDimLayers();
+ }
+ boolean result = mFirst.animateDimLayers();
+ result |= mSecond.animateDimLayers();
+ return result;
+ }
+
+ void resetDimming() {
+ if (mStack != null) {
+ mStack.resetDimmingTag();
+ return;
+ }
+ mFirst.resetDimming();
+ mSecond.resetDimming();
+ }
+
+ boolean isDimming() {
+ if (mStack != null) {
+ return mStack.isDimming();
+ }
+ boolean result = mFirst.isDimming();
+ result |= mSecond.isDimming();
+ return result;
+ }
+
+ void stopDimmingIfNeeded() {
+ if (mStack != null) {
+ mStack.stopDimmingIfNeeded();
+ return;
+ }
+ mFirst.stopDimmingIfNeeded();
+ mSecond.stopDimmingIfNeeded();
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mParent="); pw.println(mParent);
pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString());
diff --git a/services/java/com/android/server/wm/TaskStack.java b/services/java/com/android/server/wm/TaskStack.java
index 590849f..50d23a1 100644
--- a/services/java/com/android/server/wm/TaskStack.java
+++ b/services/java/com/android/server/wm/TaskStack.java
@@ -16,15 +16,25 @@
package com.android.server.wm;
+import android.graphics.Rect;
+import android.util.TypedValue;
+
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import java.io.PrintWriter;
import java.util.ArrayList;
public class TaskStack {
+ /** Amount of time in milliseconds to animate the dim surface from one value to another,
+ * when no window animation is driving it. */
+ private static final int DEFAULT_DIM_DURATION = 200;
+
/** Unique identifier */
final int mStackId;
+ /** The service */
+ private final WindowManagerService mService;
+
/** The display this stack sits under. */
private final DisplayContent mDisplayContent;
@@ -35,9 +45,29 @@ public class TaskStack {
/** The StackBox this sits in. */
StackBox mStackBox;
- TaskStack(int stackId, DisplayContent displayContent) {
+ /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */
+ final DimLayer mDimLayer;
+
+ /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */
+ WindowStateAnimator mDimWinAnimator;
+
+ /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
+ final DimLayer mAnimationBackgroundSurface;
+
+ /** The particular window with an Animation with non-zero background color. */
+ WindowStateAnimator mAnimationBackgroundAnimator;
+
+ /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
+ * then stop any dimming. */
+ boolean mDimmingTag;
+
+ TaskStack(WindowManagerService service, int stackId, DisplayContent displayContent) {
+ mService = service;
mStackId = stackId;
mDisplayContent = displayContent;
+ final int displayId = displayContent.getDisplayId();
+ mDimLayer = new DimLayer(service, displayId);
+ mAnimationBackgroundSurface = new DimLayer(service, displayId);
}
DisplayContent getDisplayContent() {
@@ -48,13 +78,6 @@ public class TaskStack {
return mTasks;
}
- ArrayList<Task> merge(TaskStack stack) {
- ArrayList<Task> taskLists = stack.mTasks;
- taskLists.addAll(mTasks);
- mTasks = taskLists;
- return taskLists;
- }
-
boolean isHomeStack() {
return mStackId == HOME_STACK_ID;
}
@@ -92,15 +115,116 @@ public class TaskStack {
}
int remove() {
+ mAnimationBackgroundSurface.destroySurface();
+ mDimLayer.destroySurface();
return mStackBox.remove();
}
- int numTokens() {
- int count = 0;
- for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
- count += mTasks.get(taskNdx).mAppTokens.size();
+ void resetAnimationBackgroundAnimator() {
+ mAnimationBackgroundAnimator = null;
+ mAnimationBackgroundSurface.hide();
+ }
+
+ private long getDimBehindFadeDuration(long duration) {
+ TypedValue tv = new TypedValue();
+ mService.mContext.getResources().getValue(
+ com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
+ if (tv.type == TypedValue.TYPE_FRACTION) {
+ duration = (long)tv.getFraction(duration, duration);
+ } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
+ duration = tv.data;
+ }
+ return duration;
+ }
+
+ boolean animateDimLayers() {
+ final int dimLayer;
+ final float dimAmount;
+ if (mDimWinAnimator == null) {
+ dimLayer = mDimLayer.getLayer();
+ dimAmount = 0;
+ } else {
+ dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
+ dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
+ }
+ final float targetAlpha = mDimLayer.getTargetAlpha();
+ if (targetAlpha != dimAmount) {
+ if (mDimWinAnimator == null) {
+ mDimLayer.hide(DEFAULT_DIM_DURATION);
+ } else {
+ long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
+ ? mDimWinAnimator.mAnimation.computeDurationHint()
+ : DEFAULT_DIM_DURATION;
+ if (targetAlpha > dimAmount) {
+ duration = getDimBehindFadeDuration(duration);
+ }
+ mDimLayer.show(dimLayer, dimAmount, duration);
+ }
+ } else if (mDimLayer.getLayer() != dimLayer) {
+ mDimLayer.setLayer(dimLayer);
+ }
+ if (mDimLayer.isAnimating()) {
+ if (!mService.okToDisplay()) {
+ // Jump to the end of the animation.
+ mDimLayer.show();
+ } else {
+ return mDimLayer.stepAnimation();
+ }
+ }
+ return false;
+ }
+
+ void resetDimmingTag() {
+ mDimmingTag = false;
+ }
+
+ void setDimmingTag() {
+ mDimmingTag = true;
+ }
+
+ boolean testDimmingTag() {
+ return mDimmingTag;
+ }
+
+ boolean isDimming() {
+ return mDimLayer.isDimming();
+ }
+
+ boolean isDimming(WindowStateAnimator winAnimator) {
+ return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
+ }
+
+ void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
+ // Only set dim params on the highest dimmed layer.
+ final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator;
+ // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
+ if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
+ || !existingDimWinAnimator.mSurfaceShown
+ || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
+ mDimWinAnimator = newWinAnimator;
}
- return count;
+ }
+
+ void stopDimmingIfNeeded() {
+ if (!mDimmingTag && isDimming()) {
+ mDimWinAnimator = null;
+ }
+ }
+
+ void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
+ int animLayer = winAnimator.mAnimLayer;
+ if (mAnimationBackgroundAnimator == null
+ || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
+ mAnimationBackgroundAnimator = winAnimator;
+ animLayer = mService.adjustAnimationBackground(winAnimator);
+ mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
+ ((color >> 24) & 0xff) / 255f, 0);
+ }
+ }
+
+ void setBounds(Rect bounds) {
+ mDimLayer.setBounds(bounds);
+ mAnimationBackgroundSurface.setBounds(bounds);
}
public void dump(String prefix, PrintWriter pw) {
@@ -108,6 +232,15 @@ public class TaskStack {
for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
pw.print(prefix); pw.println(mTasks.get(taskNdx));
}
+ if (mAnimationBackgroundSurface.isDimming()) {
+ pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
+ mAnimationBackgroundSurface.printTo(prefix + " ", pw);
+ }
+ if (mDimLayer.isDimming()) {
+ pw.print(prefix); pw.println("mDimLayer:");
+ mDimLayer.printTo(prefix, pw);
+ pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
+ }
}
@Override
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index d101602..9d6576a 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -19,7 +19,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
-import android.util.TypedValue;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManagerPolicy;
@@ -38,10 +37,6 @@ import java.util.ArrayList;
public class WindowAnimator {
private static final String TAG = "WindowAnimator";
- /** Amount of time in milliseconds to animate the dim surface from one value to another,
- * when no window animation is driving it. */
- static final int DEFAULT_DIM_DURATION = 200;
-
final WindowManagerService mService;
final Context mContext;
final WindowManagerPolicy mPolicy;
@@ -50,8 +45,6 @@ public class WindowAnimator {
final Runnable mAnimationRunnable;
- int mAdjResult;
-
/** Time of current animation step. Reset on each iteration */
long mCurrentTime;
@@ -120,18 +113,10 @@ public class WindowAnimator {
void removeDisplayLocked(final int displayId) {
final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
if (displayAnimator != null) {
- if (displayAnimator.mWindowAnimationBackgroundSurface != null) {
- displayAnimator.mWindowAnimationBackgroundSurface.destroySurface();
- displayAnimator.mWindowAnimationBackgroundSurface = null;
- }
if (displayAnimator.mScreenRotationAnimation != null) {
displayAnimator.mScreenRotationAnimation.kill();
displayAnimator.mScreenRotationAnimation = null;
}
- if (displayAnimator.mDimAnimator != null) {
- displayAnimator.mDimAnimator.destroySurface();
- displayAnimator.mDimAnimator = null;
- }
}
mDisplayContentsAnimators.delete(displayId);
@@ -173,7 +158,6 @@ public class WindowAnimator {
}
private void updateAppWindowsLocked(int displayId) {
- int i;
final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
final ArrayList<Task> tasks = displayContent.getTasks();
final int numTasks = tasks.size();
@@ -198,7 +182,7 @@ public class WindowAnimator {
final AppTokenList exitingAppTokens = displayContent.mExitingAppTokens;
final int NEAT = exitingAppTokens.size();
- for (i=0; i<NEAT; i++) {
+ for (int i = 0; i < NEAT; i++) {
final AppWindowAnimator appAnimator = exitingAppTokens.get(i).mAppAnimator;
final boolean wasAnimating = appAnimator.animation != null
&& appAnimator.animation != AppWindowAnimator.sDummyAnimation;
@@ -366,11 +350,9 @@ public class WindowAnimator {
}
private void updateWallpaperLocked(int displayId) {
- final DisplayContentsAnimator displayAnimator =
- getDisplayContentsAnimatorLocked(displayId);
+ mService.getDisplayContentLocked(displayId).resetAnimationBackgroundAnimator();
+
final WindowList windows = mService.getWindowListLocked(displayId);
- WindowStateAnimator windowAnimationBackground = null;
- int windowAnimationBackgroundColor = 0;
WindowState detachedWallpaper = null;
for (int i = windows.size() - 1; i >= 0; i--) {
@@ -391,13 +373,9 @@ public class WindowAnimator {
&& winAnimator.mAnimation.getDetachWallpaper()) {
detachedWallpaper = win;
}
- final int backgroundColor = winAnimator.mAnimation.getBackgroundColor();
- if (backgroundColor != 0) {
- if (windowAnimationBackground == null || (winAnimator.mAnimLayer <
- windowAnimationBackground.mAnimLayer)) {
- windowAnimationBackground = winAnimator;
- windowAnimationBackgroundColor = backgroundColor;
- }
+ final int color = winAnimator.mAnimation.getBackgroundColor();
+ if (color != 0) {
+ win.getStack().setAnimationBackground(winAnimator, color);
}
}
mAnimating = true;
@@ -414,13 +392,9 @@ public class WindowAnimator {
detachedWallpaper = win;
}
- final int backgroundColor = appAnimator.animation.getBackgroundColor();
- if (backgroundColor != 0) {
- if (windowAnimationBackground == null || (winAnimator.mAnimLayer <
- windowAnimationBackground.mAnimLayer)) {
- windowAnimationBackground = winAnimator;
- windowAnimationBackgroundColor = backgroundColor;
- }
+ final int color = appAnimator.animation.getBackgroundColor();
+ if (color != 0) {
+ win.getStack().setAnimationBackground(winAnimator, color);
}
}
} // end forall windows
@@ -432,31 +406,6 @@ public class WindowAnimator {
mWindowDetachedWallpaper = detachedWallpaper;
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
}
-
- if (windowAnimationBackgroundColor != 0) {
- // If the window that wants black is the current wallpaper
- // target, then the black goes *below* the wallpaper so we
- // don't cause the wallpaper to suddenly disappear.
- int animLayer = windowAnimationBackground.mAnimLayer;
- WindowState win = windowAnimationBackground.mWin;
- if (mService.mWallpaperTarget == win || mService.mLowerWallpaperTarget == win
- || mService.mUpperWallpaperTarget == win) {
- final int N = windows.size();
- for (int i = 0; i < N; i++) {
- WindowStateAnimator winAnimator = windows.get(i).mWinAnimator;
- if (winAnimator.mIsWallpaper) {
- animLayer = winAnimator.mAnimLayer;
- break;
- }
- }
- }
-
- displayAnimator.mWindowAnimationBackgroundSurface.show(
- animLayer - WindowManagerService.LAYER_OFFSET_DIM,
- ((windowAnimationBackgroundColor >> 24) & 0xff) / 255f, 0);
- } else {
- displayAnimator.mWindowAnimationBackgroundSurface.hide();
- }
}
/** See if any windows have been drawn, so they (and others associated with them) can now be
@@ -510,17 +459,6 @@ public class WindowAnimator {
updateWallpaperLocked(displayId);
}
- private long getDimBehindFadeDuration(long duration) {
- TypedValue tv = new TypedValue();
- mContext.getResources().getValue(
- com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
- if (tv.type == TypedValue.TYPE_FRACTION) {
- duration = (long)tv.getFraction(duration, duration);
- } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
- duration = tv.data;
- }
- return duration;
- }
/** Locked on mService.mWindowMap. */
private void animateLocked() {
@@ -575,49 +513,7 @@ public class WindowAnimator {
testTokenMayBeDrawnLocked(displayId);
- DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
-
- final ScreenRotationAnimation screenRotationAnimation =
- displayAnimator.mScreenRotationAnimation;
- if (screenRotationAnimation != null) {
- screenRotationAnimation.updateSurfacesInTransaction();
- }
-
- final DimLayer dimAnimator = displayAnimator.mDimAnimator;
- final WindowStateAnimator winAnimator = displayAnimator.mDimWinAnimator;
- final int dimLayer;
- final float dimAmount;
- if (winAnimator == null) {
- dimLayer = dimAnimator.getLayer();
- dimAmount = 0;
- } else {
- dimLayer = winAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
- dimAmount = winAnimator.mWin.mAttrs.dimAmount;
- }
- final float targetAlpha = dimAnimator.getTargetAlpha();
- if (targetAlpha != dimAmount) {
- if (winAnimator == null) {
- dimAnimator.hide(DEFAULT_DIM_DURATION);
- } else {
- long duration = (winAnimator.mAnimating && winAnimator.mAnimation != null)
- ? winAnimator.mAnimation.computeDurationHint()
- : DEFAULT_DIM_DURATION;
- if (targetAlpha > dimAmount) {
- duration = getDimBehindFadeDuration(duration);
- }
- dimAnimator.show(dimLayer, dimAmount, duration);
- }
- } else if (dimAnimator.getLayer() != dimLayer) {
- dimAnimator.setLayer(dimLayer);
- }
- if (dimAnimator.isAnimating()) {
- if (!mService.okToDisplay()) {
- // Jump to the end of the animation.
- dimAnimator.show();
- } else {
- mAnimating |= dimAnimator.stepAnimation();
- }
- }
+ mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers();
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
@@ -673,21 +569,6 @@ public class WindowAnimator {
}
}
- boolean isDimmingLocked(int displayId) {
- return getDisplayContentsAnimatorLocked(displayId).mDimAnimator.isDimming();
- }
-
- boolean isDimmingLocked(final WindowStateAnimator winAnimator) {
- final int displayId = winAnimator.mWin.getDisplayId();
- DisplayContentsAnimator displayAnimator =
- getDisplayContentsAnimatorLocked(displayId);
- if (displayAnimator != null) {
- return displayAnimator.mDimWinAnimator == winAnimator
- && displayAnimator.mDimAnimator.isDimming();
- }
- return false;
- }
-
static String bulkUpdateParamsToString(int bulkUpdateParams) {
StringBuilder builder = new StringBuilder(128);
if ((bulkUpdateParams & LayoutFields.SET_UPDATE_ROTATION) != 0) {
@@ -725,18 +606,6 @@ public class WindowAnimator {
pw.print(subPrefix); pw.print("Window #"); pw.print(j);
pw.print(": "); pw.println(wanim);
}
- if (displayAnimator.mWindowAnimationBackgroundSurface != null) {
- if (dumpAll || displayAnimator.mWindowAnimationBackgroundSurface.isDimming()) {
- pw.print(subPrefix); pw.println("mWindowAnimationBackgroundSurface:");
- displayAnimator.mWindowAnimationBackgroundSurface.printTo(subSubPrefix, pw);
- }
- }
- if (dumpAll || displayAnimator.mDimAnimator.isDimming()) {
- pw.print(subPrefix); pw.println("mDimAnimator:");
- displayAnimator.mDimAnimator.printTo(subSubPrefix, pw);
- pw.print(subPrefix); pw.print("mDimWinAnimator=");
- pw.println(displayAnimator.mDimWinAnimator);
- }
if (displayAnimator.mScreenRotationAnimation != null) {
pw.print(subPrefix); pw.println("mScreenRotationAnimation:");
displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw);
@@ -794,26 +663,10 @@ public class WindowAnimator {
}
}
- void setDimWinAnimatorLocked(int displayId, WindowStateAnimator newWinAnimator) {
- DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
- if (newWinAnimator == null) {
- displayAnimator.mDimWinAnimator = null;
- } else {
- // Only set dim params on the highest dimmed layer.
- final WindowStateAnimator existingDimWinAnimator = displayAnimator.mDimWinAnimator;
- // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
- if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
- || !existingDimWinAnimator.mSurfaceShown
- || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
- displayAnimator.mDimWinAnimator = newWinAnimator;
- }
- }
- }
-
private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
if (displayAnimator == null) {
- displayAnimator = new DisplayContentsAnimator(displayId);
+ displayAnimator = new DisplayContentsAnimator();
mDisplayContentsAnimators.put(displayId, displayAnimator);
}
return displayAnimator;
@@ -828,14 +681,6 @@ public class WindowAnimator {
}
private class DisplayContentsAnimator {
- DimLayer mDimAnimator = null;
- WindowStateAnimator mDimWinAnimator = null;
- DimLayer mWindowAnimationBackgroundSurface = null;
ScreenRotationAnimation mScreenRotationAnimation = null;
-
- public DisplayContentsAnimator(int displayId) {
- mDimAnimator = new DimLayer(mService, displayId);
- mWindowAnimationBackgroundSurface = new DimLayer(mService, displayId);
- }
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 6a2d3e2..e22e70c 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -34,6 +34,7 @@ import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AttributeCache;
import com.android.server.EventLogTags;
+import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.DisplayManagerService;
@@ -565,7 +566,6 @@ public class WindowManagerService extends IWindowManager.Stub
Object mLastWindowFreezeSource = null;
private Session mHoldScreen = null;
private boolean mObscured = false;
- boolean mDimming = false;
private boolean mSyswin = false;
private float mScreenBrightness = -1;
private float mButtonBrightness = -1;
@@ -701,8 +701,7 @@ public class WindowManagerService extends IWindowManager.Stub
public static WindowManagerService main(final Context context,
final PowerManagerService pm, final DisplayManagerService dm,
- final InputManagerService im,
- final Handler uiHandler, final Handler wmHandler,
+ final InputManagerService im, final Handler wmHandler,
final boolean haveInputMethods, final boolean showBootMsgs,
final boolean onlyCore) {
final WindowManagerService[] holder = new WindowManagerService[1];
@@ -710,7 +709,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void run() {
holder[0] = new WindowManagerService(context, pm, dm, im,
- uiHandler, haveInputMethods, showBootMsgs, onlyCore);
+ haveInputMethods, showBootMsgs, onlyCore);
}
}, 0);
return holder[0];
@@ -732,7 +731,6 @@ public class WindowManagerService extends IWindowManager.Stub
private WindowManagerService(Context context, PowerManagerService pm,
DisplayManagerService displayManager, InputManagerService inputManager,
- Handler uiHandler,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
mContext = context;
mHaveInputMethods = haveInputMethods;
@@ -795,7 +793,7 @@ public class WindowManagerService extends IWindowManager.Stub
mFxSession = new SurfaceSession();
mAnimator = new WindowAnimator(this);
- initPolicy(uiHandler);
+ initPolicy(UiThread.getHandler());
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
@@ -1872,6 +1870,25 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ final TaskStack targetStack =
+ mWallpaperTarget == null ? null : mWallpaperTarget.getStack();
+ if ((changed & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0 &&
+ targetStack != null && !targetStack.isHomeStack()) {
+ // If the wallpaper target is not on the home stack then make sure that all windows
+ // from other non-home stacks are above the wallpaper.
+ for (i = foundI - 1; i >= 0; --i) {
+ WindowState win = windows.get(i);
+ if (!win.isVisibleLw()) {
+ continue;
+ }
+ final TaskStack winStack = win.getStack();
+ if (winStack != null && !winStack.isHomeStack() && winStack != targetStack) {
+ windows.remove(i);
+ windows.add(foundI + 1, win);
+ }
+ }
+ }
+
if (targetChanged && DEBUG_WALLPAPER_LIGHT) {
Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
+ " lower=" + mLowerWallpaperTarget + " upper="
@@ -3591,9 +3608,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (computeScreenConfigurationLocked(mTempConfiguration)) {
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
- getDefaultDisplayContentLocked().layoutNeeded = true;
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ displayContent.layoutNeeded = true;
int anim[] = new int[2];
- if (mAnimator.isDimmingLocked(Display.DEFAULT_DISPLAY)) {
+ if (displayContent.isDimming()) {
anim[0] = anim[1] = 0;
} else {
mPolicy.selectRotationAnimationLw(anim);
@@ -3696,13 +3714,15 @@ public class WindowManagerService extends IWindowManager.Stub
/** Call while in a Surface transaction. */
void setFocusedStackLayer() {
mFocusedStackLayer = 0;
- final WindowList windows = mFocusedApp.allAppWindows;
- for (int i = windows.size() - 1; i >= 0; --i) {
- final WindowState win = windows.get(i);
- final int animLayer = win.mWinAnimator.mAnimLayer;
- if (win.mAttachedWindow == null && win.isVisibleLw() &&
- animLayer > mFocusedStackLayer) {
- mFocusedStackLayer = animLayer + LAYER_OFFSET_FOCUSED_STACK;
+ if (mFocusedApp != null) {
+ final WindowList windows = mFocusedApp.allAppWindows;
+ for (int i = windows.size() - 1; i >= 0; --i) {
+ final WindowState win = windows.get(i);
+ final int animLayer = win.mWinAnimator.mAnimLayer;
+ if (win.mAttachedWindow == null && win.isVisibleLw() &&
+ animLayer > mFocusedStackLayer) {
+ mFocusedStackLayer = animLayer + LAYER_OFFSET_FOCUSED_STACK;
+ }
}
}
if (DEBUG_LAYERS) Slog.v(TAG, "Setting FocusedStackFrame to layer=" +
@@ -4793,7 +4813,7 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent = getDefaultDisplayContentLocked();
}
TaskStack stack =
- displayContent.createStack(stackId, relativeStackId, position, weight);
+ displayContent.createStack(this, stackId, relativeStackId, position, weight);
mStackIdToStack.put(stackId, stack);
displayContent.moveStack(stack, true);
}
@@ -5627,14 +5647,16 @@ public class WindowManagerService extends IWindowManager.Stub
int[] buffer = new int[bm.getWidth() * bm.getHeight()];
bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
boolean allBlack = true;
+ final int firstColor = buffer[0];
for (int i = 0; i < buffer.length; i++) {
- if (buffer[i] != Color.BLACK) {
+ if (buffer[i] != firstColor) {
allBlack = false;
break;
}
}
if (allBlack) {
- Slog.i(TAG, "Screenshot " + appWin + " was all black! mSurfaceLayer=" +
+ Slog.i(TAG, "Screenshot " + appWin + " was monochrome(" +
+ Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
(appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null") +
" minLayer=" + minLayer + " maxLayer=" + maxLayer);
}
@@ -5826,9 +5848,10 @@ public class WindowManagerService extends IWindowManager.Stub
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION);
mWaitingForConfig = true;
- getDefaultDisplayContentLocked().layoutNeeded = true;
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ displayContent.layoutNeeded = true;
final int[] anim = new int[2];
- if (mAnimator.isDimmingLocked(Display.DEFAULT_DISPLAY)) {
+ if (displayContent.isDimming()) {
anim[0] = anim[1] = 0;
} else {
mPolicy.selectRotationAnimationLw(anim);
@@ -5846,7 +5869,6 @@ public class WindowManagerService extends IWindowManager.Stub
// the rotation animation for the new rotation.
computeScreenConfigurationLocked(null);
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (!inTransaction) {
if (SHOW_TRANSACTIONS) {
@@ -7912,8 +7934,8 @@ public class WindowManagerService extends IWindowManager.Stub
layerChanged = true;
anyLayerChanged = true;
}
- if (layerChanged && mAnimator.isDimmingLocked(winAnimator)) {
- // Force an animation pass just to update the mDimAnimator layer.
+ if (layerChanged && w.getStack().isDimming(winAnimator)) {
+ // Force an animation pass just to update the mDimLayer layer.
scheduleAnimationLocked();
}
if (DEBUG_LAYERS) Slog.v(TAG, "Assign layer " + w + ": "
@@ -8671,11 +8693,12 @@ public class WindowManagerService extends IWindowManager.Stub
if ((attrs.flags & FLAG_DIM_BEHIND) != 0
&& w.isDisplayedLw()
&& !w.mExiting) {
- mInnerFields.mDimming = true;
final WindowStateAnimator winAnimator = w.mWinAnimator;
- if (!mAnimator.isDimmingLocked(winAnimator)) {
+ final TaskStack stack = w.getStack();
+ stack.setDimmingTag();
+ if (!stack.isDimming(winAnimator)) {
if (localLOGV) Slog.v(TAG, "Win " + w + " start dimming.");
- startDimmingLocked(winAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount);
+ stack.startDimmingIfNeeded(winAnimator);
}
}
}
@@ -8844,8 +8867,8 @@ public class WindowManagerService extends IWindowManager.Stub
} while (displayContent.pendingLayoutChanges != 0);
mInnerFields.mObscured = false;
- mInnerFields.mDimming = false;
mInnerFields.mSyswin = false;
+ displayContent.resetDimming();
// Only used if default window
final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
@@ -8862,7 +8885,7 @@ public class WindowManagerService extends IWindowManager.Stub
handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
}
- if (!mInnerFields.mDimming) {
+ if (!w.getStack().testDimmingTag()) {
handleFlagDimBehind(w, innerDw, innerDh);
}
@@ -9005,9 +9028,7 @@ public class WindowManagerService extends IWindowManager.Stub
mDisplayManagerService.setDisplayHasContent(displayId, hasUniqueContent,
true /* inTraversal, must call performTraversalInTrans... below */);
- if (!mInnerFields.mDimming && mAnimator.isDimmingLocked(displayId)) {
- stopDimmingLocked(displayId);
- }
+ getDisplayContentLocked(displayId).stopDimmingIfNeeded();
if (updateAllDrawn) {
updateAllDrawnLocked(displayContent);
@@ -9415,14 +9436,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- void startDimmingLocked(final WindowStateAnimator winAnimator, final float target) {
- mAnimator.setDimWinAnimatorLocked(winAnimator.mWin.getDisplayId(), winAnimator);
- }
-
- void stopDimmingLocked(int displayId) {
- mAnimator.setDimWinAnimatorLocked(displayId, null);
- }
-
private boolean needsLayout() {
DisplayContentsIterator iterator = new DisplayContentsIterator();
while (iterator.hasNext()) {
@@ -9468,6 +9481,24 @@ public class WindowManagerService extends IWindowManager.Stub
return doRequest;
}
+ /** If a window that has an animation specifying a colored background is the current wallpaper
+ * target, then the color goes *below* the wallpaper so we don't cause the wallpaper to
+ * suddenly disappear. */
+ int adjustAnimationBackground(WindowStateAnimator winAnimator) {
+ final WindowState win = winAnimator.mWin;
+ if (mWallpaperTarget == win || mLowerWallpaperTarget == win
+ || mUpperWallpaperTarget == win) {
+ WindowList windows = win.getWindowList();
+ for (int i = windows.size() - 1; i >= 0; --i) {
+ WindowState testWin = windows.get(i);
+ if (testWin.mIsWallpaper) {
+ return testWin.mWinAnimator.mAnimLayer;
+ }
+ }
+ }
+ return winAnimator.mAnimLayer;
+ }
+
boolean reclaimSomeSurfaceMemoryLocked(WindowStateAnimator winAnimator, String operation,
boolean secure) {
final SurfaceControl surface = winAnimator.mSurfaceControl;
@@ -9829,7 +9860,7 @@ public class WindowManagerService extends IWindowManager.Stub
// TODO(multidisplay): rotation on main screen only.
DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Get rotation animation again, with new top window
- boolean isDimming = mAnimator.isDimmingLocked(Display.DEFAULT_DISPLAY);
+ boolean isDimming = displayContent.isDimming();
if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
mExitAnimId = mEnterAnimId = 0;
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 642f5ff..c45d786 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -525,13 +525,8 @@ final class WindowState implements WindowManagerPolicy.WindowState {
// Now make sure the window fits in the overall display.
Gravity.applyDisplay(mAttrs.gravity, df, mFrame);
- // Make sure the overscan, content and visible frames are inside of the
+ // Make sure the content and visible frames are inside of the
// final window frame.
- mOverscanFrame.set(Math.max(mOverscanFrame.left, mFrame.left),
- Math.max(mOverscanFrame.top, mFrame.top),
- Math.min(mOverscanFrame.right, mFrame.right),
- Math.min(mOverscanFrame.bottom, mFrame.bottom));
-
mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
Math.max(mContentFrame.top, mFrame.top),
Math.min(mContentFrame.right, mFrame.right),
@@ -689,13 +684,14 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
TaskStack getStack() {
- if (mAppToken != null) {
- Task task = mService.mTaskIdToTask.get(mAppToken.groupId);
+ AppWindowToken wtoken = mAppToken == null ? mService.mFocusedApp : mAppToken;
+ if (wtoken != null) {
+ Task task = mService.mTaskIdToTask.get(wtoken.groupId);
if (task != null) {
return task.mStack;
}
}
- return null;
+ return mDisplayContent.getHomeStack();
}
Rect getStackBounds() {
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 9e18227..8bf7788 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -1184,7 +1184,7 @@ class WindowStateAnimator {
mAnimator.setPendingLayoutChanges(displayId,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
- mService.startDimmingLocked(this, w.mExiting ? 0 : w.mAttrs.dimAmount);
+ w.getStack().startDimmingIfNeeded(this);
}
} catch (RuntimeException e) {
// If something goes wrong with the surface (such