diff options
Diffstat (limited to 'services/java/com')
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 |
