auto import from //depot/cupcake/@135843
diff --git a/core/java/android/os/AsyncResult.java b/core/java/android/os/AsyncResult.java
new file mode 100644
index 0000000..5bad09d
--- /dev/null
+++ b/core/java/android/os/AsyncResult.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.os.Message;
+
+/** @hide */
+public class AsyncResult
+{
+
+ /*************************** Instance Variables **************************/
+
+ // Expect either exception or result to be null
+ public Object userObj;
+ public Throwable exception;
+ public Object result;
+
+ /***************************** Class Methods *****************************/
+
+ /** Saves and sets m.obj */
+ public static AsyncResult
+ forMessage(Message m, Object r, Throwable ex)
+ {
+ AsyncResult ret;
+
+ ret = new AsyncResult (m.obj, r, ex);
+
+ m.obj = ret;
+
+ return ret;
+ }
+
+ /** Saves and sets m.obj */
+ public static AsyncResult
+ forMessage(Message m)
+ {
+ AsyncResult ret;
+
+ ret = new AsyncResult (m.obj, null, null);
+
+ m.obj = ret;
+
+ return ret;
+ }
+
+ /** please note, this sets m.obj to be this */
+ public
+ AsyncResult (Object uo, Object r, Throwable ex)
+ {
+ userObj = uo;
+ result = r;
+ exception = ex;
+ }
+}
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
new file mode 100644
index 0000000..ee4e897
--- /dev/null
+++ b/core/java/android/os/AsyncTask.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2008 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 android.os;
+
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
+ * perform background operations and publish results on the UI thread without
+ * having to manipulate threads and/or handlers.</p>
+ *
+ * <p>An asynchronous task is defined by a computation that runs on a background thread and
+ * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
+ * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
+ * and 4 steps, called <code>begin</code>, <code>doInBackground</code>,
+ * <code>processProgress<code> and <code>end</code>.</p>
+ *
+ * <h2>Usage</h2>
+ * <p>AsyncTask must be subclassed to be used. The subclass will override at least
+ * one method ({@link #doInBackground}), and most often will override a
+ * second one ({@link #onPostExecute}.)</p>
+ *
+ * <p>Here is an example of subclassing:</p>
+ * <pre class="prettyprint">
+ * private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
+ * protected Long doInBackground(URL... urls) {
+ * int count = urls.length;
+ * long totalSize = 0;
+ * for (int i = 0; i < count; i++) {
+ * totalSize += Downloader.downloadFile(urls[i]);
+ * publishProgress((int) ((i / (float) count) * 100));
+ * }
+ * return totalSize;
+ * }
+ *
+ * protected void onProgressUpdate(Integer... progress) {
+ * setProgressPercent(progress[0]);
+ * }
+ *
+ * protected void onPostExecute(Long result) {
+ * showDialog("Downloaded " + result + " bytes");
+ * }
+ * }
+ * </pre>
+ *
+ * <p>Once created, a task is executed very simply:</p>
+ * <pre class="prettyprint">
+ * new DownloadFilesTask().execute(url1, url2, url3);
+ * </pre>
+ *
+ * <h2>AsyncTask's generic types</h2>
+ * <p>The three types used by an asynchronous task are the following:</p>
+ * <ol>
+ * <li><code>Params</code>, the type of the parameters sent to the task upon
+ * execution.</li>
+ * <li><code>Progress</code>, the type of the progress units published during
+ * the background computation.</li>
+ * <li><code>Result</code>, the type of the result of the background
+ * computation.</li>
+ * </ol>
+ * <p>Not all types are always used by am asynchronous task. To mark a type as unused,
+ * simply use the type {@link Void}:</p>
+ * <pre>
+ * private class MyTask extends AsyncTask<Void, Void, Void) { ... }
+ * </pre>
+ *
+ * <h2>The 4 steps</h2>
+ * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
+ * <ol>
+ * <li>{@link #onPreExecute()}, invoked on the UI thread immediately after the task
+ * is executed. This step is normally used to setup the task, for instance by
+ * showing a progress bar in the user interface.</li>
+ * <li>{@link #doInBackground}, invoked on the background thread
+ * immediately after {@link #onPreExecute()} finishes executing. This step is used
+ * to perform background computation that can take a long time. The parameters
+ * of the asynchronous task are passed to this step. The result of the computation must
+ * be returned by this step and will be passed back to the last step. This step
+ * can also use {@link #publishProgress} to publish one or more units
+ * of progress. These values are published on the UI thread, in the
+ * {@link #onProgressUpdate} step.</li>
+ * <li>{@link #onProgressUpdate}, invoked on the UI thread after a
+ * call to {@link #publishProgress}. The timing of the execution is
+ * undefined. This method is used to display any form of progress in the user
+ * interface while the background computation is still executing. For instance,
+ * it can be used to animate a progress bar or show logs in a text field.</li>
+ * <li>{@link #onPostExecute}, invoked on the UI thread after the background
+ * computation finishes. The result of the background computation is passed to
+ * this step as a parameter.</li>
+ * </ol>
+ *
+ * <h2>Threading rules</h2>
+ * <p>There are a few threading rules that must be followed for this class to
+ * work properly:</p>
+ * <ul>
+ * <li>The task instance must be created on the UI thread.</li>
+ * <li>{@link #execute} must be invoked on the UI thread.</li>
+ * <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
+ * {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
+ * <li>The task can be executed only once (an exception will be thrown if
+ * a second execution is attempted.)</li>
+ * </ul>
+ */
+public abstract class AsyncTask<Params, Progress, Result> {
+ private static final String LOG_TAG = "AsyncTask";
+
+ private static final int CORE_POOL_SIZE = 1;
+ private static final int MAXIMUM_POOL_SIZE = 10;
+ private static final int KEEP_ALIVE = 10;
+
+ private static final BlockingQueue<Runnable> sWorkQueue =
+ new LinkedBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE);
+
+ private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+ private final AtomicInteger mCount = new AtomicInteger(1);
+
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
+ }
+ };
+
+ private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
+ MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
+
+ private static final int MESSAGE_POST_RESULT = 0x1;
+ private static final int MESSAGE_POST_PROGRESS = 0x2;
+ private static final int MESSAGE_POST_CANCEL = 0x3;
+
+ private static final InternalHandler sHandler = new InternalHandler();
+
+ private final WorkerRunnable<Params, Result> mWorker;
+ private final FutureTask<Result> mFuture;
+
+ private volatile Status mStatus = Status.PENDING;
+
+ /**
+ * Indicates the current status of the task. Each status will be set only once
+ * during the lifetime of a task.
+ */
+ public enum Status {
+ /**
+ * Indicates that the task has not been executed yet.
+ */
+ PENDING,
+ /**
+ * Indicates that the task is running.
+ */
+ RUNNING,
+ /**
+ * Indicates that {@link AsyncTask#onPostExecute} has finished.
+ */
+ FINISHED,
+ }
+
+ /**
+ * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
+ */
+ public AsyncTask() {
+ mWorker = new WorkerRunnable<Params, Result>() {
+ public Result call() throws Exception {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ return doInBackground(mParams);
+ }
+ };
+
+ mFuture = new FutureTask<Result>(mWorker) {
+ @Override
+ protected void done() {
+ Message message;
+ Result result = null;
+
+ try {
+ result = get();
+ } catch (InterruptedException e) {
+ android.util.Log.w(LOG_TAG, e);
+ } catch (ExecutionException e) {
+ throw new RuntimeException("An error occured while executing doInBackground()",
+ e.getCause());
+ } catch (CancellationException e) {
+ message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
+ new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
+ message.sendToTarget();
+ return;
+ } catch (Throwable t) {
+ throw new RuntimeException("An error occured while executing "
+ + "doInBackground()", t);
+ }
+
+ message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
+ new AsyncTaskResult<Result>(AsyncTask.this, result));
+ message.sendToTarget();
+ }
+ };
+ }
+
+ /**
+ * Returns the current status of this task.
+ *
+ * @return The current status.
+ */
+ public final Status getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * Override this method to perform a computation on a background thread. The
+ * specified parameters are the parameters passed to {@link #execute}
+ * by the caller of this task.
+ *
+ * This method can call {@link #publishProgress} to publish updates
+ * on the UI thread.
+ *
+ * @param params The parameters of the task.
+ *
+ * @return A result, defined by the subclass of this task.
+ *
+ * @see #onPreExecute()
+ * @see #onPostExecute
+ * @see #publishProgress
+ */
+ protected abstract Result doInBackground(Params... params);
+
+ /**
+ * Runs on the UI thread before {@link #doInBackground}.
+ *
+ * @see #onPostExecute
+ * @see #doInBackground
+ */
+ protected void onPreExecute() {
+ }
+
+ /**
+ * Runs on the UI thread after {@link #doInBackground}. The
+ * specified result is the value returned by {@link #doInBackground}
+ * or null if the task was cancelled or an exception occured.
+ *
+ * @param result The result of the operation computed by {@link #doInBackground}.
+ *
+ * @see #onPreExecute
+ * @see #doInBackground
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
+ protected void onPostExecute(Result result) {
+ }
+
+ /**
+ * Runs on the UI thread after {@link #publishProgress} is invoked.
+ * The specified values are the values passed to {@link #publishProgress}.
+ *
+ * @param values The values indicating progress.
+ *
+ * @see #publishProgress
+ * @see #doInBackground
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
+ protected void onProgressUpdate(Progress... values) {
+ }
+
+ /**
+ * Runs on the UI thread after {@link #cancel(boolean)} is invoked.
+ *
+ * @see #cancel(boolean)
+ * @see #isCancelled()
+ */
+ protected void onCancelled() {
+ }
+
+ /**
+ * Returns <tt>true</tt> if this task was cancelled before it completed
+ * normally.
+ *
+ * @return <tt>true</tt> if task was cancelled before it completed
+ *
+ * @see #cancel(boolean)
+ */
+ public final boolean isCancelled() {
+ return mFuture.isCancelled();
+ }
+
+ /**
+ * Attempts to cancel execution of this task. This attempt will
+ * fail if the task has already completed, already been cancelled,
+ * or could not be cancelled for some other reason. If successful,
+ * and this task has not started when <tt>cancel</tt> is called,
+ * this task should never run. If the task has already started,
+ * then the <tt>mayInterruptIfRunning</tt> parameter determines
+ * whether the thread executing this task should be interrupted in
+ * an attempt to stop the task.
+ *
+ * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
+ * task should be interrupted; otherwise, in-progress tasks are allowed
+ * to complete.
+ *
+ * @return <tt>false</tt> if the task could not be cancelled,
+ * typically because it has already completed normally;
+ * <tt>true</tt> otherwise
+ *
+ * @see #isCancelled()
+ * @see #onCancelled()
+ */
+ public final boolean cancel(boolean mayInterruptIfRunning) {
+ return mFuture.cancel(mayInterruptIfRunning);
+ }
+
+ /**
+ * Waits if necessary for the computation to complete, and then
+ * retrieves its result.
+ *
+ * @return The computed result.
+ *
+ * @throws CancellationException If the computation was cancelled.
+ * @throws ExecutionException If the computation threw an exception.
+ * @throws InterruptedException If the current thread was interrupted
+ * while waiting.
+ */
+ public final Result get() throws InterruptedException, ExecutionException {
+ return mFuture.get();
+ }
+
+ /**
+ * Waits if necessary for at most the given time for the computation
+ * to complete, and then retrieves its result.
+ *
+ * @param timeout Time to wait before cancelling the operation.
+ * @param unit The time unit for the timeout.
+ *
+ * @return The computed result.
+ *
+ * @throws CancellationException If the computation was cancelled.
+ * @throws ExecutionException If the computation threw an exception.
+ * @throws InterruptedException If the current thread was interrupted
+ * while waiting.
+ * @throws TimeoutException If the wait timed out.
+ */
+ public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
+ ExecutionException, TimeoutException {
+ return mFuture.get(timeout, unit);
+ }
+
+ /**
+ * Executes the task with the specified parameters. The task returns
+ * itself (this) so that the caller can keep a reference to it.
+ *
+ * This method must be invoked on the UI thread.
+ *
+ * @param params The parameters of the task.
+ *
+ * @return This instance of AsyncTask.
+ *
+ * @throws IllegalStateException If {@link #getStatus()} returns either
+ * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+ */
+ public final AsyncTask<Params, Progress, Result> execute(Params... params) {
+ if (mStatus != Status.PENDING) {
+ switch (mStatus) {
+ case RUNNING:
+ throw new IllegalStateException("Cannot execute task:"
+ + " the task is already running.");
+ case FINISHED:
+ throw new IllegalStateException("Cannot execute task:"
+ + " the task has already been executed "
+ + "(a task can be executed only once)");
+ }
+ }
+
+ mStatus = Status.RUNNING;
+
+ onPreExecute();
+
+ mWorker.mParams = params;
+ sExecutor.execute(mFuture);
+
+ return this;
+ }
+
+ /**
+ * This method can be invoked from {@link #doInBackground} to
+ * publish updates on the UI thread while the background computation is
+ * still running. Each call to this method will trigger the execution of
+ * {@link #onProgressUpdate} on the UI thread.
+ *
+ * @param values The progress values to update the UI with.
+ *
+ * @see #onProgressUpdate
+ * @see #doInBackground
+ */
+ protected final void publishProgress(Progress... values) {
+ sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
+ new AsyncTaskResult<Progress>(this, values)).sendToTarget();
+ }
+
+ private void finish(Result result) {
+ onPostExecute(result);
+ mStatus = Status.FINISHED;
+ }
+
+ private static class InternalHandler extends Handler {
+ @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncTaskResult result = (AsyncTaskResult) msg.obj;
+ switch (msg.what) {
+ case MESSAGE_POST_RESULT:
+ // There is only one result
+ result.mTask.finish(result.mData[0]);
+ break;
+ case MESSAGE_POST_PROGRESS:
+ result.mTask.onProgressUpdate(result.mData);
+ break;
+ case MESSAGE_POST_CANCEL:
+ result.mTask.onCancelled();
+ break;
+ }
+ }
+ }
+
+ private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
+ Params[] mParams;
+ }
+
+ @SuppressWarnings({"RawUseOfParameterizedType"})
+ private static class AsyncTaskResult<Data> {
+ final AsyncTask mTask;
+ final Data[] mData;
+
+ AsyncTaskResult(AsyncTask task, Data... data) {
+ mTask = task;
+ mData = data;
+ }
+ }
+}
diff --git a/core/java/android/os/BadParcelableException.java b/core/java/android/os/BadParcelableException.java
new file mode 100644
index 0000000..a1c5bb2
--- /dev/null
+++ b/core/java/android/os/BadParcelableException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+import android.util.AndroidRuntimeException;
+
+/**
+ * The object you are calling has died, because its hosting process
+ * no longer exists.
+ */
+public class BadParcelableException extends AndroidRuntimeException {
+ public BadParcelableException(String msg) {
+ super(msg);
+ }
+ public BadParcelableException(Exception cause) {
+ super(cause);
+ }
+}
diff --git a/core/java/android/os/Base64Utils.java b/core/java/android/os/Base64Utils.java
new file mode 100644
index 0000000..684a469
--- /dev/null
+++ b/core/java/android/os/Base64Utils.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * {@hide}
+ */
+public class Base64Utils
+{
+ // TODO add encode api here if possible
+
+ public static byte [] decodeBase64(String data) {
+ return decodeBase64Native(data);
+ }
+ private static native byte[] decodeBase64Native(String data);
+}
+
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
new file mode 100644
index 0000000..8f1a756
--- /dev/null
+++ b/core/java/android/os/BatteryManager.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 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 android.os;
+
+/**
+ * The BatteryManager class contains strings and constants used for values
+ * in the ACTION_BATTERY_CHANGED Intent.
+ */
+public class BatteryManager {
+
+ // values for "status" field in the ACTION_BATTERY_CHANGED Intent
+ public static final int BATTERY_STATUS_UNKNOWN = 1;
+ public static final int BATTERY_STATUS_CHARGING = 2;
+ public static final int BATTERY_STATUS_DISCHARGING = 3;
+ public static final int BATTERY_STATUS_NOT_CHARGING = 4;
+ public static final int BATTERY_STATUS_FULL = 5;
+
+ // values for "health" field in the ACTION_BATTERY_CHANGED Intent
+ public static final int BATTERY_HEALTH_UNKNOWN = 1;
+ public static final int BATTERY_HEALTH_GOOD = 2;
+ public static final int BATTERY_HEALTH_OVERHEAT = 3;
+ public static final int BATTERY_HEALTH_DEAD = 4;
+ public static final int BATTERY_HEALTH_OVER_VOLTAGE = 5;
+ public static final int BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6;
+
+ // values of the "plugged" field in the ACTION_BATTERY_CHANGED intent.
+ // These must be powers of 2.
+ /** Power source is an AC charger. */
+ public static final int BATTERY_PLUGGED_AC = 1;
+ /** Power source is a USB port. */
+ public static final int BATTERY_PLUGGED_USB = 2;
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
new file mode 100644
index 0000000..7590bfe
--- /dev/null
+++ b/core/java/android/os/BatteryStats.java
@@ -0,0 +1,828 @@
+package android.os;
+
+import java.io.PrintWriter;
+import java.util.Formatter;
+import java.util.Map;
+
+import android.util.Log;
+import android.util.Printer;
+import android.util.SparseArray;
+
+/**
+ * A class providing access to battery usage statistics, including information on
+ * wakelocks, processes, packages, and services. All times are represented in microseconds
+ * except where indicated otherwise.
+ * @hide
+ */
+public abstract class BatteryStats implements Parcelable {
+
+ private static final boolean LOCAL_LOGV = false;
+
+ /**
+ * A constant indicating a partial wake lock timer.
+ */
+ public static final int WAKE_TYPE_PARTIAL = 0;
+
+ /**
+ * A constant indicating a full wake lock timer.
+ */
+ public static final int WAKE_TYPE_FULL = 1;
+
+ /**
+ * A constant indicating a window wake lock timer.
+ */
+ public static final int WAKE_TYPE_WINDOW = 2;
+
+ /**
+ * A constant indicating a sensor timer.
+ *
+ * {@hide}
+ */
+ public static final int SENSOR = 3;
+
+ /**
+ * Include all of the data in the stats, including previously saved data.
+ */
+ public static final int STATS_TOTAL = 0;
+
+ /**
+ * Include only the last run in the stats.
+ */
+ public static final int STATS_LAST = 1;
+
+ /**
+ * Include only the current run in the stats.
+ */
+ public static final int STATS_CURRENT = 2;
+
+ /**
+ * Include only the run since the last time the device was unplugged in the stats.
+ */
+ public static final int STATS_UNPLUGGED = 3;
+
+ /**
+ * Bump the version on this if the checkin format changes.
+ */
+ private static final int BATTERY_STATS_CHECKIN_VERSION = 1;
+
+ // TODO: Update this list if you add/change any stats above.
+ private static final String[] STAT_NAMES = { "total", "last", "current", "unplugged" };
+
+ private static final String APK_DATA = "apk";
+ private static final String PROCESS_DATA = "process";
+ private static final String SENSOR_DATA = "sensor";
+ private static final String WAKELOCK_DATA = "wakelock";
+ private static final String NETWORK_DATA = "network";
+ private static final String BATTERY_DATA = "battery";
+ private static final String MISC_DATA = "misc";
+
+ private final StringBuilder mFormatBuilder = new StringBuilder(8);
+ private final Formatter mFormatter = new Formatter(mFormatBuilder);
+
+ /**
+ * State for keeping track of timing information.
+ */
+ public static abstract class Timer {
+
+ /**
+ * Returns the count associated with this Timer for the
+ * selected type of statistics.
+ *
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
+ */
+ public abstract int getCount(int which);
+
+ /**
+ * Returns the total time in microseconds associated with this Timer for the
+ * selected type of statistics.
+ *
+ * @param batteryRealtime system realtime on battery in microseconds
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
+ * @return a time in microseconds
+ */
+ public abstract long getTotalTime(long batteryRealtime, int which);
+
+ /**
+ * Temporary for debugging.
+ */
+ public abstract void logState();
+ }
+
+ /**
+ * The statistics associated with a particular uid.
+ */
+ public static abstract class Uid {
+
+ /**
+ * Returns a mapping containing wakelock statistics.
+ *
+ * @return a Map from Strings to Uid.Wakelock objects.
+ */
+ public abstract Map<String, ? extends Wakelock> getWakelockStats();
+
+ /**
+ * The statistics associated with a particular wake lock.
+ */
+ public static abstract class Wakelock {
+ public abstract Timer getWakeTime(int type);
+ }
+
+ /**
+ * Returns a mapping containing sensor statistics.
+ *
+ * @return a Map from Integer sensor ids to Uid.Sensor objects.
+ */
+ public abstract Map<Integer, ? extends Sensor> getSensorStats();
+
+ /**
+ * Returns a mapping containing process statistics.
+ *
+ * @return a Map from Strings to Uid.Proc objects.
+ */
+ public abstract Map<String, ? extends Proc> getProcessStats();
+
+ /**
+ * Returns a mapping containing package statistics.
+ *
+ * @return a Map from Strings to Uid.Pkg objects.
+ */
+ public abstract Map<String, ? extends Pkg> getPackageStats();
+
+ /**
+ * {@hide}
+ */
+ public abstract int getUid();
+
+ /**
+ * {@hide}
+ */
+ public abstract long getTcpBytesReceived(int which);
+
+ /**
+ * {@hide}
+ */
+ public abstract long getTcpBytesSent(int which);
+
+ public static abstract class Sensor {
+ // Magic sensor number for the GPS.
+ public static final int GPS = -10000;
+
+ public abstract int getHandle();
+
+ public abstract Timer getSensorTime();
+ }
+
+ /**
+ * The statistics associated with a particular process.
+ */
+ public static abstract class Proc {
+
+ /**
+ * Returns the total time (in 1/100 sec) spent executing in user code.
+ *
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ */
+ public abstract long getUserTime(int which);
+
+ /**
+ * Returns the total time (in 1/100 sec) spent executing in system code.
+ *
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ */
+ public abstract long getSystemTime(int which);
+
+ /**
+ * Returns the number of times the process has been started.
+ *
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ */
+ public abstract int getStarts(int which);
+ }
+
+ /**
+ * The statistics associated with a particular package.
+ */
+ public static abstract class Pkg {
+
+ /**
+ * Returns the number of times this package has done something that could wake up the
+ * device from sleep.
+ *
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ */
+ public abstract int getWakeups(int which);
+
+ /**
+ * Returns a mapping containing service statistics.
+ */
+ public abstract Map<String, ? extends Serv> getServiceStats();
+
+ /**
+ * The statistics associated with a particular service.
+ */
+ public abstract class Serv {
+
+ /**
+ * Returns the amount of time spent started.
+ *
+ * @param batteryUptime elapsed uptime on battery in microseconds.
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ * @return
+ */
+ public abstract long getStartTime(long batteryUptime, int which);
+
+ /**
+ * Returns the total number of times startService() has been called.
+ *
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ */
+ public abstract int getStarts(int which);
+
+ /**
+ * Returns the total number times the service has been launched.
+ *
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ */
+ public abstract int getLaunches(int which);
+ }
+ }
+ }
+
+ /**
+ * Returns the number of times the device has been started.
+ */
+ public abstract int getStartCount();
+
+ /**
+ * Returns the time in milliseconds that the screen has been on while the device was
+ * running on battery.
+ *
+ * {@hide}
+ */
+ public abstract long getScreenOnTime(long batteryRealtime, int which);
+
+ /**
+ * Returns the time in milliseconds that the phone has been on while the device was
+ * running on battery.
+ *
+ * {@hide}
+ */
+ public abstract long getPhoneOnTime(long batteryRealtime, int which);
+
+ /**
+ * Return whether we are currently running on battery.
+ */
+ public abstract boolean getIsOnBattery();
+
+ /**
+ * Returns a SparseArray containing the statistics for each uid.
+ */
+ public abstract SparseArray<? extends Uid> getUidStats();
+
+ /**
+ * Returns the current battery uptime in microseconds.
+ *
+ * @param curTime the amount of elapsed realtime in microseconds.
+ */
+ public abstract long getBatteryUptime(long curTime);
+
+ /**
+ * Returns the current battery realtime in microseconds.
+ *
+ * @param curTime the amount of elapsed realtime in microseconds.
+ */
+ public abstract long getBatteryRealtime(long curTime);
+
+ /**
+ * Returns the total, last, or current battery uptime in microseconds.
+ *
+ * @param curTime the elapsed realtime in microseconds.
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ */
+ public abstract long computeBatteryUptime(long curTime, int which);
+
+ /**
+ * Returns the total, last, or current battery realtime in microseconds.
+ *
+ * @param curTime the current elapsed realtime in microseconds.
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ */
+ public abstract long computeBatteryRealtime(long curTime, int which);
+
+ /**
+ * Returns the total, last, or current uptime in microseconds.
+ *
+ * @param curTime the current elapsed realtime in microseconds.
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ */
+ public abstract long computeUptime(long curTime, int which);
+
+ /**
+ * Returns the total, last, or current realtime in microseconds.
+ * *
+ * @param curTime the current elapsed realtime in microseconds.
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ */
+ public abstract long computeRealtime(long curTime, int which);
+
+ private final static void formatTime(StringBuilder out, long seconds) {
+ long days = seconds / (60 * 60 * 24);
+ if (days != 0) {
+ out.append(days);
+ out.append("d ");
+ }
+ long used = days * 60 * 60 * 24;
+
+ long hours = (seconds - used) / (60 * 60);
+ if (hours != 0 || used != 0) {
+ out.append(hours);
+ out.append("h ");
+ }
+ used += hours * 60 * 60;
+
+ long mins = (seconds-used) / 60;
+ if (mins != 0 || used != 0) {
+ out.append(mins);
+ out.append("m ");
+ }
+ used += mins * 60;
+
+ if (seconds != 0 || used != 0) {
+ out.append(seconds-used);
+ out.append("s ");
+ }
+ }
+
+ private final static String formatTime(long time) {
+ long sec = time / 100;
+ StringBuilder sb = new StringBuilder();
+ formatTime(sb, sec);
+ sb.append((time - (sec * 100)) * 10);
+ sb.append("ms ");
+ return sb.toString();
+ }
+
+ private final static String formatTimeMs(long time) {
+ long sec = time / 1000;
+ StringBuilder sb = new StringBuilder();
+ formatTime(sb, sec);
+ sb.append(time - (sec * 1000));
+ sb.append("ms ");
+ return sb.toString();
+ }
+
+ private final String formatRatioLocked(long num, long den) {
+ if (den == 0L) {
+ return "---%";
+ }
+ float perc = ((float)num) / ((float)den) * 100;
+ mFormatBuilder.setLength(0);
+ mFormatter.format("%.1f%%", perc);
+ return mFormatBuilder.toString();
+ }
+
+ /**
+ *
+ * @param sb a StringBuilder object.
+ * @param timer a Timer object contining the wakelock times.
+ * @param batteryRealtime the current on-battery time in microseconds.
+ * @param name the name of the wakelock.
+ * @param which which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ * @param linePrefix a String to be prepended to each line of output.
+ * @return the line prefix
+ */
+ private static final String printWakeLock(StringBuilder sb, Timer timer,
+ long batteryRealtime, String name, int which, String linePrefix) {
+
+ if (timer != null) {
+ // Convert from microseconds to milliseconds with rounding
+ long totalTimeMicros = timer.getTotalTime(batteryRealtime, which);
+ long totalTimeMillis = (totalTimeMicros + 500) / 1000;
+
+ int count = timer.getCount(which);
+ if (totalTimeMillis != 0) {
+ sb.append(linePrefix);
+ sb.append(formatTimeMs(totalTimeMillis));
+ sb.append(name);
+ sb.append(' ');
+ sb.append('(');
+ sb.append(count);
+ sb.append(" times)");
+ return ", ";
+ }
+ }
+ return linePrefix;
+ }
+
+ /**
+ * Checkin version of wakelock printer. Prints simple comma-separated list.
+ *
+ * @param sb a StringBuilder object.
+ * @param timer a Timer object contining the wakelock times.
+ * @param now the current time in microseconds.
+ * @param name the name of the wakelock.
+ * @param which which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+ * @param linePrefix a String to be prepended to each line of output.
+ * @return the line prefix
+ */
+ private static final String printWakeLockCheckin(StringBuilder sb, Timer timer, long now,
+ String name, int which, String linePrefix) {
+ long totalTimeMicros = 0;
+ int count = 0;
+ if (timer != null) {
+ totalTimeMicros = timer.getTotalTime(now, which);
+ count = timer.getCount(which);
+ }
+ sb.append(linePrefix);
+ sb.append((totalTimeMicros + 500) / 1000); // microseconds to milliseconds with rounding
+ sb.append(',');
+ sb.append(name);
+ sb.append(',');
+ sb.append(count);
+ return ",";
+ }
+
+ /**
+ * Dump a comma-separated line of values for terse checkin mode.
+ *
+ * @param pw the PageWriter to dump log to
+ * @param category category of data (e.g. "total", "last", "unplugged", "current" )
+ * @param type type of data (e.g. "wakelock", "sensor", "process", "apk" , "process", "network")
+ * @param args type-dependent data arguments
+ */
+ private static final void dumpLine(PrintWriter pw, int uid, String category, String type,
+ Object... args ) {
+ pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+ pw.print(uid); pw.print(',');
+ pw.print(category); pw.print(',');
+ pw.print(type);
+
+ for (Object arg : args) {
+ pw.print(',');
+ pw.print(arg);
+ }
+ pw.print('\n');
+ }
+
+ /**
+ * Checkin server version of dump to produce more compact, computer-readable log.
+ *
+ * NOTE: all times are expressed in 'ms'.
+ * @param fd
+ * @param pw
+ * @param which
+ */
+ private final void dumpCheckinLocked(PrintWriter pw, int which) {
+ final long rawUptime = SystemClock.uptimeMillis() * 1000;
+ final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
+ final long batteryUptime = getBatteryUptime(rawUptime);
+ final long batteryRealtime = getBatteryRealtime(rawRealtime);
+ final long whichBatteryUptime = computeBatteryUptime(rawUptime, which);
+ final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which);
+ final long totalRealtime = computeRealtime(rawRealtime, which);
+ final long totalUptime = computeUptime(rawUptime, which);
+ final long screenOnTime = getScreenOnTime(batteryRealtime, which);
+ final long phoneOnTime = getPhoneOnTime(batteryRealtime, which);
+
+ StringBuilder sb = new StringBuilder(128);
+
+ String category = STAT_NAMES[which];
+
+ // Dump "battery" stat
+ dumpLine(pw, 0 /* uid */, category, BATTERY_DATA,
+ which == STATS_TOTAL ? getStartCount() : "N/A",
+ whichBatteryUptime / 1000, whichBatteryRealtime / 1000,
+ totalUptime / 1000, totalRealtime / 1000);
+
+ // Dump misc stats
+ dumpLine(pw, 0 /* uid */, category, MISC_DATA,
+ screenOnTime / 1000, phoneOnTime / 1000);
+
+ SparseArray<? extends Uid> uidStats = getUidStats();
+ final int NU = uidStats.size();
+ for (int iu = 0; iu < NU; iu++) {
+ final int uid = uidStats.keyAt(iu);
+ Uid u = uidStats.valueAt(iu);
+ // Dump Network stats per uid, if any
+ long rx = u.getTcpBytesReceived(which);
+ long tx = u.getTcpBytesSent(which);
+ if (rx > 0 || tx > 0) dumpLine(pw, uid, category, NETWORK_DATA, rx, tx);
+
+ Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
+ if (wakelocks.size() > 0) {
+ for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
+ : wakelocks.entrySet()) {
+ Uid.Wakelock wl = ent.getValue();
+ String linePrefix = "";
+ sb.setLength(0);
+ linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_FULL), batteryRealtime,
+ "full", which, linePrefix);
+ linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), batteryRealtime,
+ "partial", which, linePrefix);
+ linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), batteryRealtime,
+ "window", which, linePrefix);
+
+ // Only log if we had at lease one wakelock...
+ if (sb.length() > 0) {
+ dumpLine(pw, uid, category, WAKELOCK_DATA, ent.getKey(), sb.toString());
+ }
+ }
+ }
+
+ Map<Integer, ? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+ if (sensors.size() > 0) {
+ for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> ent
+ : sensors.entrySet()) {
+ Uid.Sensor se = ent.getValue();
+ int sensorNumber = ent.getKey();
+ Timer timer = se.getSensorTime();
+ if (timer != null) {
+ // Convert from microseconds to milliseconds with rounding
+ long totalTime = (timer.getTotalTime(batteryRealtime, which) + 500) / 1000;
+ int count = timer.getCount(which);
+ if (totalTime != 0) {
+ dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count);
+ }
+ }
+ }
+ }
+
+ Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
+ if (processStats.size() > 0) {
+ for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
+ : processStats.entrySet()) {
+ Uid.Proc ps = ent.getValue();
+
+ long userTime = ps.getUserTime(which);
+ long systemTime = ps.getSystemTime(which);
+ int starts = ps.getStarts(which);
+
+ if (userTime != 0 || systemTime != 0 || starts != 0) {
+ dumpLine(pw, uid, category, PROCESS_DATA,
+ ent.getKey(), // proc
+ userTime * 10, // cpu time in ms
+ systemTime * 10, // user time in ms
+ starts); // process starts
+ }
+ }
+ }
+
+ Map<String, ? extends BatteryStats.Uid.Pkg> packageStats = u.getPackageStats();
+ if (packageStats.size() > 0) {
+ for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg> ent
+ : packageStats.entrySet()) {
+
+ Uid.Pkg ps = ent.getValue();
+ int wakeups = ps.getWakeups(which);
+ Map<String, ? extends Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
+ for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg.Serv> sent
+ : serviceStats.entrySet()) {
+ BatteryStats.Uid.Pkg.Serv ss = sent.getValue();
+ long startTime = ss.getStartTime(batteryUptime, which);
+ int starts = ss.getStarts(which);
+ int launches = ss.getLaunches(which);
+ if (startTime != 0 || starts != 0 || launches != 0) {
+ dumpLine(pw, uid, category, APK_DATA,
+ wakeups, // wakeup alarms
+ ent.getKey(), // Apk
+ sent.getKey(), // service
+ startTime / 1000, // time spent started, in ms
+ starts,
+ launches);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private final void dumpLocked(Printer pw, String prefix, int which) {
+ final long rawUptime = SystemClock.uptimeMillis() * 1000;
+ final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
+ final long batteryUptime = getBatteryUptime(rawUptime);
+ final long batteryRealtime = getBatteryUptime(rawRealtime);
+
+ final long whichBatteryUptime = computeBatteryUptime(rawUptime, which);
+ final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which);
+ final long totalRealtime = computeRealtime(rawRealtime, which);
+ final long totalUptime = computeUptime(rawUptime, which);
+
+ StringBuilder sb = new StringBuilder(128);
+
+ pw.println(prefix
+ + " Time on battery: " + formatTimeMs(whichBatteryUptime / 1000)
+ + "(" + formatRatioLocked(whichBatteryUptime, totalRealtime)
+ + ") uptime, "
+ + formatTimeMs(whichBatteryRealtime / 1000) + "("
+ + formatRatioLocked(whichBatteryRealtime, totalRealtime)
+ + ") realtime");
+ pw.println(prefix
+ + " Total: "
+ + formatTimeMs(totalUptime / 1000)
+ + "uptime, "
+ + formatTimeMs(totalRealtime / 1000)
+ + "realtime");
+
+ long screenOnTime = getScreenOnTime(batteryRealtime, which);
+ long phoneOnTime = getPhoneOnTime(batteryRealtime, which);
+ pw.println(prefix
+ + " Time with screen on: " + formatTimeMs(screenOnTime / 1000)
+ + "(" + formatRatioLocked(screenOnTime, whichBatteryRealtime)
+ + "), time with phone on: " + formatTimeMs(phoneOnTime / 1000)
+ + "(" + formatRatioLocked(phoneOnTime, whichBatteryRealtime) + ")");
+
+ pw.println(" ");
+
+ SparseArray<? extends Uid> uidStats = getUidStats();
+ final int NU = uidStats.size();
+ for (int iu=0; iu<NU; iu++) {
+ final int uid = uidStats.keyAt(iu);
+ Uid u = uidStats.valueAt(iu);
+ pw.println(prefix + " #" + uid + ":");
+ boolean uidActivity = false;
+
+ long tcpReceived = u.getTcpBytesReceived(which);
+ long tcpSent = u.getTcpBytesSent(which);
+ if (tcpReceived != 0 || tcpSent != 0) {
+ pw.println(prefix + " Network: " + tcpReceived + " bytes received, "
+ + tcpSent + " bytes sent");
+ }
+
+ Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
+ if (wakelocks.size() > 0) {
+ for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
+ : wakelocks.entrySet()) {
+ Uid.Wakelock wl = ent.getValue();
+ String linePrefix = ": ";
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Wake lock ");
+ sb.append(ent.getKey());
+ linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_FULL), batteryRealtime,
+ "full", which, linePrefix);
+ linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), batteryRealtime,
+ "partial", which, linePrefix);
+ linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), batteryRealtime,
+ "window", which, linePrefix);
+ if (!linePrefix.equals(": ")) {
+ sb.append(" realtime");
+ } else {
+ sb.append(": (nothing executed)");
+ }
+ pw.println(sb.toString());
+ uidActivity = true;
+ }
+ }
+
+ Map<Integer, ? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+ if (sensors.size() > 0) {
+ for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> ent
+ : sensors.entrySet()) {
+ Uid.Sensor se = ent.getValue();
+ int sensorNumber = ent.getKey();
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Sensor ");
+ int handle = se.getHandle();
+ if (handle == Uid.Sensor.GPS) {
+ sb.append("GPS");
+ } else {
+ sb.append(handle);
+ }
+ sb.append(": ");
+
+ Timer timer = se.getSensorTime();
+ if (timer != null) {
+ // Convert from microseconds to milliseconds with rounding
+ long totalTime = (timer.getTotalTime(batteryRealtime, which) + 500) / 1000;
+ int count = timer.getCount(which);
+ //timer.logState();
+ if (totalTime != 0) {
+ sb.append(formatTimeMs(totalTime));
+ sb.append("realtime (");
+ sb.append(count);
+ sb.append(" times)");
+ } else {
+ sb.append("(not used)");
+ }
+ } else {
+ sb.append("(not used)");
+ }
+
+ pw.println(sb.toString());
+ uidActivity = true;
+ }
+ }
+
+ Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
+ if (processStats.size() > 0) {
+ for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
+ : processStats.entrySet()) {
+ Uid.Proc ps = ent.getValue();
+ long userTime;
+ long systemTime;
+ int starts;
+
+ userTime = ps.getUserTime(which);
+ systemTime = ps.getSystemTime(which);
+ starts = ps.getStarts(which);
+
+ if (userTime != 0 || systemTime != 0 || starts != 0) {
+ pw.println(prefix + " Proc " + ent.getKey() + ":");
+ pw.println(prefix + " CPU: " + formatTime(userTime) + "user + "
+ + formatTime(systemTime) + "kernel");
+ pw.println(prefix + " " + starts + " process starts");
+ uidActivity = true;
+ }
+ }
+ }
+
+ Map<String, ? extends BatteryStats.Uid.Pkg> packageStats = u.getPackageStats();
+ if (packageStats.size() > 0) {
+ for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg> ent
+ : packageStats.entrySet()) {
+ pw.println(prefix + " Apk " + ent.getKey() + ":");
+ boolean apkActivity = false;
+ Uid.Pkg ps = ent.getValue();
+ int wakeups = ps.getWakeups(which);
+ if (wakeups != 0) {
+ pw.println(prefix + " " + wakeups + " wakeup alarms");
+ apkActivity = true;
+ }
+ Map<String, ? extends Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
+ if (serviceStats.size() > 0) {
+ for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg.Serv> sent
+ : serviceStats.entrySet()) {
+ BatteryStats.Uid.Pkg.Serv ss = sent.getValue();
+ long startTime = ss.getStartTime(batteryUptime, which);
+ int starts = ss.getStarts(which);
+ int launches = ss.getLaunches(which);
+ if (startTime != 0 || starts != 0 || launches != 0) {
+ pw.println(prefix + " Service " + sent.getKey() + ":");
+ pw.println(prefix + " Created for: "
+ + formatTimeMs(startTime / 1000)
+ + " uptime");
+ pw.println(prefix + " Starts: " + starts
+ + ", launches: " + launches);
+ apkActivity = true;
+ }
+ }
+ }
+ if (!apkActivity) {
+ pw.println(prefix + " (nothing executed)");
+ }
+ uidActivity = true;
+ }
+ }
+ if (!uidActivity) {
+ pw.println(prefix + " (nothing executed)");
+ }
+ }
+ }
+
+ /**
+ * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
+ *
+ * @param pw a Printer to receive the dump output.
+ */
+ @SuppressWarnings("unused")
+ public void dumpLocked(Printer pw) {
+ pw.println("Total Statistics (Current and Historic):");
+ pw.println(" System starts: " + getStartCount()
+ + ", currently on battery: " + getIsOnBattery());
+ dumpLocked(pw, "", STATS_TOTAL);
+ pw.println("");
+ pw.println("Last Run Statistics (Previous run of system):");
+ dumpLocked(pw, "", STATS_LAST);
+ pw.println("");
+ pw.println("Current Battery Statistics (Currently running system):");
+ dumpLocked(pw, "", STATS_CURRENT);
+ pw.println("");
+ pw.println("Unplugged Statistics (Since last unplugged from power):");
+ dumpLocked(pw, "", STATS_UNPLUGGED);
+ }
+
+ @SuppressWarnings("unused")
+ public void dumpCheckinLocked(PrintWriter pw, String[] args) {
+ boolean isUnpluggedOnly = false;
+
+ for (String arg : args) {
+ if ("-u".equals(arg)) {
+ if (LOCAL_LOGV) Log.v("BatteryStats", "Dumping unplugged data");
+ isUnpluggedOnly = true;
+ }
+ }
+
+ if (isUnpluggedOnly) {
+ dumpCheckinLocked(pw, STATS_UNPLUGGED);
+ }
+ else {
+ dumpCheckinLocked(pw, STATS_TOTAL);
+ dumpCheckinLocked(pw, STATS_LAST);
+ dumpCheckinLocked(pw, STATS_UNPLUGGED);
+ dumpCheckinLocked(pw, STATS_CURRENT);
+ }
+ }
+
+}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
new file mode 100644
index 0000000..df10c6a
--- /dev/null
+++ b/core/java/android/os/Binder.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.util.Config;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Modifier;
+
+/**
+ * Base class for a remotable object, the core part of a lightweight
+ * remote procedure call mechanism defined by {@link IBinder}.
+ * This class is an implementation of IBinder that provides
+ * the standard support creating a local implementation of such an object.
+ *
+ * <p>Most developers will not implement this class directly, instead using the
+ * <a href="{@docRoot}guide/developing/tools/aidl.html">aidl</a> tool to describe the desired
+ * interface, having it generate the appropriate Binder subclass. You can,
+ * however, derive directly from Binder to implement your own custom RPC
+ * protocol or simply instantiate a raw Binder object directly to use as a
+ * token that can be shared across processes.
+ *
+ * @see IBinder
+ */
+public class Binder implements IBinder {
+ /*
+ * Set this flag to true to detect anonymous, local or member classes
+ * that extend this Binder class and that are not static. These kind
+ * of classes can potentially create leaks.
+ */
+ private static final boolean FIND_POTENTIAL_LEAKS = false;
+ private static final String TAG = "Binder";
+
+ private int mObject;
+ private IInterface mOwner;
+ private String mDescriptor;
+
+ /**
+ * Return the ID of the process that sent you the current transaction
+ * that is being processed. This pid can be used with higher-level
+ * system services to determine its identity and check permissions.
+ * If the current thread is not currently executing an incoming transaction,
+ * then its own pid is returned.
+ */
+ public static final native int getCallingPid();
+
+ /**
+ * Return the ID of the user assigned to the process that sent you the
+ * current transaction that is being processed. This uid can be used with
+ * higher-level system services to determine its identity and check
+ * permissions. If the current thread is not currently executing an
+ * incoming transaction, then its own uid is returned.
+ */
+ public static final native int getCallingUid();
+
+ /**
+ * Reset the identity of the incoming IPC to the local process. This can
+ * be useful if, while handling an incoming call, you will be calling
+ * on interfaces of other objects that may be local to your process and
+ * need to do permission checks on the calls coming into them (so they
+ * will check the permission of your own local process, and not whatever
+ * process originally called you).
+ *
+ * @return Returns an opaque token that can be used to restore the
+ * original calling identity by passing it to
+ * {@link #restoreCallingIdentity(long)}.
+ *
+ * @see #getCallingPid()
+ * @see #getCallingUid()
+ * @see #restoreCallingIdentity(long)
+ */
+ public static final native long clearCallingIdentity();
+
+ /**
+ * Restore the identity of the incoming IPC back to a previously identity
+ * that was returned by {@link #clearCallingIdentity}.
+ *
+ * @param token The opaque token that was previously returned by
+ * {@link #clearCallingIdentity}.
+ *
+ * @see #clearCallingIdentity
+ */
+ public static final native void restoreCallingIdentity(long token);
+
+ /**
+ * Flush any Binder commands pending in the current thread to the kernel
+ * driver. This can be
+ * useful to call before performing an operation that may block for a long
+ * time, to ensure that any pending object references have been released
+ * in order to prevent the process from holding on to objects longer than
+ * it needs to.
+ */
+ public static final native void flushPendingCommands();
+
+ /**
+ * Add the calling thread to the IPC thread pool. This function does
+ * not return until the current process is exiting.
+ */
+ public static final native void joinThreadPool();
+
+ /**
+ * Default constructor initializes the object.
+ */
+ public Binder() {
+ init();
+
+ if (FIND_POTENTIAL_LEAKS) {
+ final Class<? extends Binder> klass = getClass();
+ if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
+ (klass.getModifiers() & Modifier.STATIC) == 0) {
+ Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
+ klass.getCanonicalName());
+ }
+ }
+ }
+
+ /**
+ * Convenience method for associating a specific interface with the Binder.
+ * After calling, queryLocalInterface() will be implemented for you
+ * to return the given owner IInterface when the corresponding
+ * descriptor is requested.
+ */
+ public void attachInterface(IInterface owner, String descriptor) {
+ mOwner = owner;
+ mDescriptor = descriptor;
+ }
+
+ /**
+ * Default implementation returns an empty interface name.
+ */
+ public String getInterfaceDescriptor() {
+ return mDescriptor;
+ }
+
+ /**
+ * Default implementation always returns true -- if you got here,
+ * the object is alive.
+ */
+ public boolean pingBinder() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Note that if you're calling on a local binder, this always returns true
+ * because your process is alive if you're calling it.
+ */
+ public boolean isBinderAlive() {
+ return true;
+ }
+
+ /**
+ * Use information supplied to attachInterface() to return the
+ * associated IInterface if it matches the requested
+ * descriptor.
+ */
+ public IInterface queryLocalInterface(String descriptor) {
+ if (mDescriptor.equals(descriptor)) {
+ return mOwner;
+ }
+ return null;
+ }
+
+ /**
+ * Default implementation is a stub that returns false. You will want
+ * to override this to do the appropriate unmarshalling of transactions.
+ *
+ * <p>If you want to call this, call transact().
+ */
+ protected boolean onTransact(int code, Parcel data, Parcel reply,
+ int flags) throws RemoteException {
+ if (code == INTERFACE_TRANSACTION) {
+ reply.writeString(getInterfaceDescriptor());
+ return true;
+ } else if (code == DUMP_TRANSACTION) {
+ ParcelFileDescriptor fd = data.readFileDescriptor();
+ String[] args = data.readStringArray();
+ if (fd != null) {
+ try {
+ dump(fd.getFileDescriptor(), args);
+ } finally {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Implemented to call the more convenient version
+ * {@link #dump(FileDescriptor, PrintWriter, String[])}.
+ */
+ public void dump(FileDescriptor fd, String[] args) {
+ FileOutputStream fout = new FileOutputStream(fd);
+ PrintWriter pw = new PrintWriter(fout);
+ try {
+ dump(fd, pw, args);
+ } finally {
+ pw.flush();
+ }
+ }
+
+ /**
+ * Print the object's state into the given stream.
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param fout The file to which you should dump your state. This will be
+ * closed for you after you return.
+ * @param args additional arguments to the dump request.
+ */
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ }
+
+ /**
+ * Default implementation rewinds the parcels and calls onTransact. On
+ * the remote side, transact calls into the binder to do the IPC.
+ */
+ public final boolean transact(int code, Parcel data, Parcel reply,
+ int flags) throws RemoteException {
+ if (Config.LOGV) Log.v("Binder", "Transact: " + code + " to " + this);
+ if (data != null) {
+ data.setDataPosition(0);
+ }
+ boolean r = onTransact(code, data, reply, flags);
+ if (reply != null) {
+ reply.setDataPosition(0);
+ }
+ return r;
+ }
+
+ /**
+ * Local implementation is a no-op.
+ */
+ public void linkToDeath(DeathRecipient recipient, int flags) {
+ }
+
+ /**
+ * Local implementation is a no-op.
+ */
+ public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
+ return true;
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ destroy();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private native final void init();
+ private native final void destroy();
+ private boolean execTransact(int code, int dataObj, int replyObj,
+ int flags) {
+ Parcel data = Parcel.obtain(dataObj);
+ Parcel reply = Parcel.obtain(replyObj);
+ // theoretically, we should call transact, which will call onTransact,
+ // but all that does is rewind it, and we just got these from an IPC,
+ // so we'll just call it directly.
+ boolean res;
+ try {
+ res = onTransact(code, data, reply, flags);
+ } catch (RemoteException e) {
+ reply.writeException(e);
+ res = true;
+ } catch (RuntimeException e) {
+ reply.writeException(e);
+ res = true;
+ }
+ reply.recycle();
+ data.recycle();
+ return res;
+ }
+}
+
+final class BinderProxy implements IBinder {
+ public native boolean pingBinder();
+ public native boolean isBinderAlive();
+
+ public IInterface queryLocalInterface(String descriptor) {
+ return null;
+ }
+
+ public native String getInterfaceDescriptor() throws RemoteException;
+ public native boolean transact(int code, Parcel data, Parcel reply,
+ int flags) throws RemoteException;
+ public native void linkToDeath(DeathRecipient recipient, int flags)
+ throws RemoteException;
+ public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeFileDescriptor(fd);
+ data.writeStringArray(args);
+ try {
+ transact(DUMP_TRANSACTION, data, null, 0);
+ } finally {
+ data.recycle();
+ }
+ }
+
+ BinderProxy() {
+ mSelf = new WeakReference(this);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ destroy();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private native final void destroy();
+
+ private static final void sendDeathNotice(DeathRecipient recipient) {
+ if (Config.LOGV) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
+ try {
+ recipient.binderDied();
+ }
+ catch (RuntimeException exc) {
+ Log.w("BinderNative", "Uncaught exception from death notification",
+ exc);
+ }
+ }
+
+ final private WeakReference mSelf;
+ private int mObject;
+}
diff --git a/core/java/android/os/Broadcaster.java b/core/java/android/os/Broadcaster.java
new file mode 100644
index 0000000..96dc61a
--- /dev/null
+++ b/core/java/android/os/Broadcaster.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/** @hide */
+public class Broadcaster
+{
+ public Broadcaster()
+ {
+ }
+
+ /**
+ * Sign up for notifications about something.
+ *
+ * When this broadcaster pushes a message with senderWhat in the what field,
+ * target will be sent a copy of that message with targetWhat in the what field.
+ */
+ public void request(int senderWhat, Handler target, int targetWhat)
+ {
+ synchronized (this) {
+ Registration r = null;
+ if (mReg == null) {
+ r = new Registration();
+ r.senderWhat = senderWhat;
+ r.targets = new Handler[1];
+ r.targetWhats = new int[1];
+ r.targets[0] = target;
+ r.targetWhats[0] = targetWhat;
+ mReg = r;
+ r.next = r;
+ r.prev = r;
+ } else {
+ // find its place in the map
+ Registration start = mReg;
+ r = start;
+ do {
+ if (r.senderWhat >= senderWhat) {
+ break;
+ }
+ r = r.next;
+ } while (r != start);
+ int n;
+ if (r.senderWhat != senderWhat) {
+ // we didn't find a senderWhat match, but r is right
+ // after where it goes
+ Registration reg = new Registration();
+ reg.senderWhat = senderWhat;
+ reg.targets = new Handler[1];
+ reg.targetWhats = new int[1];
+ reg.next = r;
+ reg.prev = r.prev;
+ r.prev.next = reg;
+ r.prev = reg;
+
+ if (r == mReg && r.senderWhat > reg.senderWhat) {
+ mReg = reg;
+ }
+
+ r = reg;
+ n = 0;
+ } else {
+ n = r.targets.length;
+ Handler[] oldTargets = r.targets;
+ int[] oldWhats = r.targetWhats;
+ // check for duplicates, and don't do it if we are dup.
+ for (int i=0; i<n; i++) {
+ if (oldTargets[i] == target && oldWhats[i] == targetWhat) {
+ return;
+ }
+ }
+ r.targets = new Handler[n+1];
+ System.arraycopy(oldTargets, 0, r.targets, 0, n);
+ r.targetWhats = new int[n+1];
+ System.arraycopy(oldWhats, 0, r.targetWhats, 0, n);
+ }
+ r.targets[n] = target;
+ r.targetWhats[n] = targetWhat;
+ }
+ }
+ }
+
+ /**
+ * Unregister for notifications for this senderWhat/target/targetWhat tuple.
+ */
+ public void cancelRequest(int senderWhat, Handler target, int targetWhat)
+ {
+ synchronized (this) {
+ Registration start = mReg;
+ Registration r = start;
+
+ if (r == null) {
+ return;
+ }
+
+ do {
+ if (r.senderWhat >= senderWhat) {
+ break;
+ }
+ r = r.next;
+ } while (r != start);
+
+ if (r.senderWhat == senderWhat) {
+ Handler[] targets = r.targets;
+ int[] whats = r.targetWhats;
+ int oldLen = targets.length;
+ for (int i=0; i<oldLen; i++) {
+ if (targets[i] == target && whats[i] == targetWhat) {
+ r.targets = new Handler[oldLen-1];
+ r.targetWhats = new int[oldLen-1];
+ if (i > 0) {
+ System.arraycopy(targets, 0, r.targets, 0, i);
+ System.arraycopy(whats, 0, r.targetWhats, 0, i);
+ }
+
+ int remainingLen = oldLen-i-1;
+ if (remainingLen != 0) {
+ System.arraycopy(targets, i+1, r.targets, i,
+ remainingLen);
+ System.arraycopy(whats, i+1, r.targetWhats, i,
+ remainingLen);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * For debugging purposes, print the registrations to System.out
+ */
+ public void dumpRegistrations()
+ {
+ synchronized (this) {
+ Registration start = mReg;
+ System.out.println("Broadcaster " + this + " {");
+ if (start != null) {
+ Registration r = start;
+ do {
+ System.out.println(" senderWhat=" + r.senderWhat);
+ int n = r.targets.length;
+ for (int i=0; i<n; i++) {
+ System.out.println(" [" + r.targetWhats[i]
+ + "] " + r.targets[i]);
+ }
+ r = r.next;
+ } while (r != start);
+ }
+ System.out.println("}");
+ }
+ }
+
+ /**
+ * Send out msg. Anyone who has registered via the request() method will be
+ * sent the message.
+ */
+ public void broadcast(Message msg)
+ {
+ synchronized (this) {
+ if (mReg == null) {
+ return;
+ }
+
+ int senderWhat = msg.what;
+ Registration start = mReg;
+ Registration r = start;
+ do {
+ if (r.senderWhat >= senderWhat) {
+ break;
+ }
+ r = r.next;
+ } while (r != start);
+ if (r.senderWhat == senderWhat) {
+ Handler[] targets = r.targets;
+ int[] whats = r.targetWhats;
+ int n = targets.length;
+ for (int i=0; i<n; i++) {
+ Handler target = targets[i];
+ Message m = Message.obtain();
+ m.copyFrom(msg);
+ m.what = whats[i];
+ target.sendMessage(m);
+ }
+ }
+ }
+ }
+
+ private class Registration
+ {
+ Registration next;
+ Registration prev;
+
+ int senderWhat;
+ Handler[] targets;
+ int[] targetWhats;
+ }
+ private Registration mReg;
+}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
new file mode 100644
index 0000000..467c17f
--- /dev/null
+++ b/core/java/android/os/Build.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Information about the current build, extracted from system properties.
+ */
+public class Build {
+ /** Value used for when a build property is unknown. */
+ private static final String UNKNOWN = "unknown";
+
+ /** Either a changelist number, or a label like "M4-rc20". */
+ public static final String ID = getString("ro.build.id");
+
+ /** A build ID string meant for displaying to the user */
+ public static final String DISPLAY = getString("ro.build.display.id");
+
+ /** The name of the overall product. */
+ public static final String PRODUCT = getString("ro.product.name");
+
+ /** The name of the industrial design. */
+ public static final String DEVICE = getString("ro.product.device");
+
+ /** The name of the underlying board, like "goldfish". */
+ public static final String BOARD = getString("ro.product.board");
+
+ /** The brand (e.g., carrier) the software is customized for, if any. */
+ public static final String BRAND = getString("ro.product.brand");
+
+ /** The end-user-visible name for the end product. */
+ public static final String MODEL = getString("ro.product.model");
+
+ /** Various version strings. */
+ public static class VERSION {
+ /**
+ * The internal value used by the underlying source control to
+ * represent this build. E.g., a perforce changelist number
+ * or a git hash.
+ */
+ public static final String INCREMENTAL = getString("ro.build.version.incremental");
+
+ /**
+ * The user-visible version string. E.g., "1.0" or "3.4b5".
+ */
+ public static final String RELEASE = getString("ro.build.version.release");
+
+ /**
+ * The user-visible SDK version of the framework. It is an integer starting at 1.
+ */
+ public static final String SDK = getString("ro.build.version.sdk");
+ }
+
+ /** The type of build, like "user" or "eng". */
+ public static final String TYPE = getString("ro.build.type");
+
+ /** Comma-separated tags describing the build, like "unsigned,debug". */
+ public static final String TAGS = getString("ro.build.tags");
+
+ /** A string that uniquely identifies this build. Do not attempt to parse this value. */
+ public static final String FINGERPRINT = getString("ro.build.fingerprint");
+
+ // The following properties only make sense for internal engineering builds.
+ public static final long TIME = getLong("ro.build.date.utc") * 1000;
+ public static final String USER = getString("ro.build.user");
+ public static final String HOST = getString("ro.build.host");
+
+ private static String getString(String property) {
+ return SystemProperties.get(property, UNKNOWN);
+ }
+
+ private static long getLong(String property) {
+ try {
+ return Long.parseLong(SystemProperties.get(property));
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+}
diff --git a/core/java/android/os/Bundle.aidl b/core/java/android/os/Bundle.aidl
new file mode 100644
index 0000000..b9e1224
--- /dev/null
+++ b/core/java/android/os/Bundle.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/os/Bundle.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable Bundle;
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
new file mode 100644
index 0000000..b669fa2
--- /dev/null
+++ b/core/java/android/os/Bundle.java
@@ -0,0 +1,1452 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A mapping from String values to various Parcelable types.
+ *
+ */
+public final class Bundle implements Parcelable, Cloneable {
+ private static final String LOG_TAG = "Bundle";
+ public static final Bundle EMPTY;
+
+ static {
+ EMPTY = new Bundle();
+ EMPTY.mMap = Collections.unmodifiableMap(new HashMap<String, Object>());
+ }
+
+ // Invariant - exactly one of mMap / mParcelledData will be null
+ // (except inside a call to unparcel)
+
+ /* package */ Map<String, Object> mMap = null;
+
+ /*
+ * If mParcelledData is non-null, then mMap will be null and the
+ * data are stored as a Parcel containing a Bundle. When the data
+ * are unparcelled, mParcelledData willbe set to null.
+ */
+ /* package */ Parcel mParcelledData = null;
+
+ private boolean mHasFds = false;
+ private boolean mFdsKnown = true;
+
+ /**
+ * The ClassLoader used when unparcelling data from mParcelledData.
+ */
+ private ClassLoader mClassLoader;
+
+ /**
+ * Constructs a new, empty Bundle.
+ */
+ public Bundle() {
+ mMap = new HashMap<String, Object>();
+ mClassLoader = getClass().getClassLoader();
+ }
+
+ /**
+ * Constructs a Bundle whose data is stored as a Parcel. The data
+ * will be unparcelled on first contact, using the assigned ClassLoader.
+ *
+ * @param parcelledData a Parcel containing a Bundle
+ */
+ Bundle(Parcel parcelledData) {
+ readFromParcel(parcelledData);
+ }
+
+ /**
+ * Constructs a new, empty Bundle that uses a specific ClassLoader for
+ * instantiating Parcelable and Serializable objects.
+ *
+ * @param loader An explicit ClassLoader to use when instantiating objects
+ * inside of the Bundle.
+ */
+ public Bundle(ClassLoader loader) {
+ mMap = new HashMap<String, Object>();
+ mClassLoader = loader;
+ }
+
+ /**
+ * Constructs a new, empty Bundle sized to hold the given number of
+ * elements. The Bundle will grow as needed.
+ *
+ * @param capacity the initial capacity of the Bundle
+ */
+ public Bundle(int capacity) {
+ mMap = new HashMap<String, Object>(capacity);
+ mClassLoader = getClass().getClassLoader();
+ }
+
+ /**
+ * Constructs a Bundle containing a copy of the mappings from the given
+ * Bundle.
+ *
+ * @param b a Bundle to be copied.
+ */
+ public Bundle(Bundle b) {
+ if (b.mParcelledData != null) {
+ mParcelledData = Parcel.obtain();
+ mParcelledData.appendFrom(b.mParcelledData, 0, b.mParcelledData.dataSize());
+ mParcelledData.setDataPosition(0);
+ } else {
+ mParcelledData = null;
+ }
+
+ if (b.mMap != null) {
+ mMap = new HashMap<String, Object>(b.mMap);
+ } else {
+ mMap = null;
+ }
+
+ mHasFds = b.mHasFds;
+ mFdsKnown = b.mFdsKnown;
+ mClassLoader = b.mClassLoader;
+ }
+
+ /**
+ * Changes the ClassLoader this Bundle uses when instantiating objects.
+ *
+ * @param loader An explicit ClassLoader to use when instantiating objects
+ * inside of the Bundle.
+ */
+ public void setClassLoader(ClassLoader loader) {
+ mClassLoader = loader;
+ }
+
+ /**
+ * Clones the current Bundle. The internal map is cloned, but the keys and
+ * values to which it refers are copied by reference.
+ */
+ @Override
+ public Object clone() {
+ return new Bundle(this);
+ }
+
+ /**
+ * If the underlying data are stored as a Parcel, unparcel them
+ * using the currently assigned class loader.
+ */
+ /* package */ synchronized void unparcel() {
+ if (mParcelledData == null) {
+ return;
+ }
+
+ mParcelledData.setDataPosition(0);
+ Bundle b = mParcelledData.readBundleUnpacked(mClassLoader);
+ mMap = b.mMap;
+
+ mHasFds = mParcelledData.hasFileDescriptors();
+ mFdsKnown = true;
+
+ mParcelledData.recycle();
+ mParcelledData = null;
+ }
+
+ /**
+ * Returns the number of mappings contained in this Bundle.
+ *
+ * @return the number of mappings as an int.
+ */
+ public int size() {
+ unparcel();
+ return mMap.size();
+ }
+
+ /**
+ * Returns true if the mapping of this Bundle is empty, false otherwise.
+ */
+ public boolean isEmpty() {
+ unparcel();
+ return mMap.isEmpty();
+ }
+
+ /**
+ * Removes all elements from the mapping of this Bundle.
+ */
+ public void clear() {
+ unparcel();
+ mMap.clear();
+ mHasFds = false;
+ mFdsKnown = true;
+ }
+
+ /**
+ * Returns true if the given key is contained in the mapping
+ * of this Bundle.
+ *
+ * @param key a String key
+ * @return true if the key is part of the mapping, false otherwise
+ */
+ public boolean containsKey(String key) {
+ unparcel();
+ return mMap.containsKey(key);
+ }
+
+ /**
+ * Returns the entry with the given key as an object.
+ *
+ * @param key a String key
+ * @return an Object, or null
+ */
+ public Object get(String key) {
+ unparcel();
+ return mMap.get(key);
+ }
+
+ /**
+ * Removes any entry with the given key from the mapping of this Bundle.
+ *
+ * @param key a String key
+ */
+ public void remove(String key) {
+ unparcel();
+ mMap.remove(key);
+ }
+
+ /**
+ * Inserts all mappings from the given Bundle into this Bundle.
+ *
+ * @param map a Bundle
+ */
+ public void putAll(Bundle map) {
+ unparcel();
+ map.unparcel();
+ mMap.putAll(map.mMap);
+
+ // fd state is now known if and only if both bundles already knew
+ mHasFds |= map.mHasFds;
+ mFdsKnown = mFdsKnown && map.mFdsKnown;
+ }
+
+ /**
+ * Returns a Set containing the Strings used as keys in this Bundle.
+ *
+ * @return a Set of String keys
+ */
+ public Set<String> keySet() {
+ unparcel();
+ return mMap.keySet();
+ }
+
+ /**
+ * Reports whether the bundle contains any parcelled file descriptors.
+ */
+ public boolean hasFileDescriptors() {
+ if (!mFdsKnown) {
+ boolean fdFound = false; // keep going until we find one or run out of data
+
+ if (mParcelledData != null) {
+ if (mParcelledData.hasFileDescriptors()) {
+ fdFound = true;
+ }
+ } else {
+ // It's been unparcelled, so we need to walk the map
+ Iterator<Map.Entry<String, Object>> iter = mMap.entrySet().iterator();
+ while (!fdFound && iter.hasNext()) {
+ Object obj = iter.next().getValue();
+ if (obj instanceof Parcelable) {
+ if ((((Parcelable)obj).describeContents()
+ & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+ fdFound = true;
+ break;
+ }
+ } else if (obj instanceof Parcelable[]) {
+ Parcelable[] array = (Parcelable[]) obj;
+ for (int n = array.length - 1; n >= 0; n--) {
+ if ((array[n].describeContents()
+ & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+ fdFound = true;
+ break;
+ }
+ }
+ } else if (obj instanceof SparseArray) {
+ SparseArray<? extends Parcelable> array =
+ (SparseArray<? extends Parcelable>) obj;
+ for (int n = array.size() - 1; n >= 0; n--) {
+ if ((array.get(n).describeContents()
+ & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+ fdFound = true;
+ break;
+ }
+ }
+ } else if (obj instanceof ArrayList) {
+ ArrayList array = (ArrayList) obj;
+ // an ArrayList here might contain either Strings or
+ // Parcelables; only look inside for Parcelables
+ if ((array.size() > 0)
+ && (array.get(0) instanceof Parcelable)) {
+ for (int n = array.size() - 1; n >= 0; n--) {
+ Parcelable p = (Parcelable) array.get(n);
+ if (p != null && ((p.describeContents()
+ & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
+ fdFound = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ mHasFds = fdFound;
+ mFdsKnown = true;
+ }
+ return mHasFds;
+ }
+
+ /**
+ * Inserts a Boolean value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a Boolean, or null
+ */
+ public void putBoolean(String key, boolean value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a byte value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a byte
+ */
+ public void putByte(String key, byte value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a char value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a char, or null
+ */
+ public void putChar(String key, char value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a short value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a short
+ */
+ public void putShort(String key, short value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts an int value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value an int, or null
+ */
+ public void putInt(String key, int value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a long value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a long
+ */
+ public void putLong(String key, long value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a float value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a float
+ */
+ public void putFloat(String key, float value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a double value into the mapping of this Bundle, replacing
+ * any existing value for the given key.
+ *
+ * @param key a String, or null
+ * @param value a double
+ */
+ public void putDouble(String key, double value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a String value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a String, or null
+ */
+ public void putString(String key, String value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a CharSequence value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a CharSequence, or null
+ */
+ public void putCharSequence(String key, CharSequence value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a Parcelable value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a Parcelable object, or null
+ */
+ public void putParcelable(String key, Parcelable value) {
+ unparcel();
+ mMap.put(key, value);
+ mFdsKnown = false;
+ }
+
+ /**
+ * Inserts an array of Parcelable values into the mapping of this Bundle,
+ * replacing any existing value for the given key. Either key or value may
+ * be null.
+ *
+ * @param key a String, or null
+ * @param value an array of Parcelable objects, or null
+ */
+ public void putParcelableArray(String key, Parcelable[] value) {
+ unparcel();
+ mMap.put(key, value);
+ mFdsKnown = false;
+ }
+
+ /**
+ * Inserts a List of Parcelable values into the mapping of this Bundle,
+ * replacing any existing value for the given key. Either key or value may
+ * be null.
+ *
+ * @param key a String, or null
+ * @param value an ArrayList of Parcelable objects, or null
+ */
+ public void putParcelableArrayList(String key,
+ ArrayList<? extends Parcelable> value) {
+ unparcel();
+ mMap.put(key, value);
+ mFdsKnown = false;
+ }
+
+ /**
+ * Inserts a SparceArray of Parcelable values into the mapping of this
+ * Bundle, replacing any existing value for the given key. Either key
+ * or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a SparseArray of Parcelable objects, or null
+ */
+ public void putSparseParcelableArray(String key,
+ SparseArray<? extends Parcelable> value) {
+ unparcel();
+ mMap.put(key, value);
+ mFdsKnown = false;
+ }
+
+ /**
+ * Inserts an ArrayList<Integer> value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value an ArrayList<Integer> object, or null
+ */
+ public void putIntegerArrayList(String key, ArrayList<Integer> value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts an ArrayList<String> value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value an ArrayList<String> object, or null
+ */
+ public void putStringArrayList(String key, ArrayList<String> value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a Serializable value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a Serializable object, or null
+ */
+ public void putSerializable(String key, Serializable value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a boolean array value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a boolean array object, or null
+ */
+ public void putBooleanArray(String key, boolean[] value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a byte array value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a byte array object, or null
+ */
+ public void putByteArray(String key, byte[] value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a short array value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a short array object, or null
+ */
+ public void putShortArray(String key, short[] value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a char array value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a char array object, or null
+ */
+ public void putCharArray(String key, char[] value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts an int array value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value an int array object, or null
+ */
+ public void putIntArray(String key, int[] value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a long array value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a long array object, or null
+ */
+ public void putLongArray(String key, long[] value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a float array value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a float array object, or null
+ */
+ public void putFloatArray(String key, float[] value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a double array value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a double array object, or null
+ */
+ public void putDoubleArray(String key, double[] value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a String array value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a String array object, or null
+ */
+ public void putStringArray(String key, String[] value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts a Bundle value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a Bundle object, or null
+ */
+ public void putBundle(String key, Bundle value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Inserts an IBinder value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value an IBinder object, or null
+ *
+ * @deprecated
+ * @hide
+ */
+ @Deprecated
+ public void putIBinder(String key, IBinder value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
+ * Returns the value associated with the given key, or false if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a boolean value
+ */
+ public boolean getBoolean(String key) {
+ unparcel();
+ return getBoolean(key, false);
+ }
+
+ // Log a message if the value was non-null but not of the expected type
+ private void typeWarning(String key, Object value, String className,
+ Object defaultValue, ClassCastException e) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Key ");
+ sb.append(key);
+ sb.append(" expected ");
+ sb.append(className);
+ sb.append(" but value was a ");
+ sb.append(value.getClass().getName());
+ sb.append(". The default value ");
+ sb.append(defaultValue);
+ sb.append(" was returned.");
+ Log.w(LOG_TAG, sb.toString());
+ Log.w(LOG_TAG, "Attempt to cast generated internal exception:", e);
+ }
+
+ private void typeWarning(String key, Object value, String className,
+ ClassCastException e) {
+ typeWarning(key, value, className, "<null>", e);
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a boolean value
+ */
+ public boolean getBoolean(String key, boolean defaultValue) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return defaultValue;
+ }
+ try {
+ return (Boolean) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Boolean", defaultValue, e);
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or (byte) 0 if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a byte value
+ */
+ public byte getByte(String key) {
+ unparcel();
+ return getByte(key, (byte) 0);
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a byte value
+ */
+ public Byte getByte(String key, byte defaultValue) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return defaultValue;
+ }
+ try {
+ return (Byte) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Byte", defaultValue, e);
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or false if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a char value
+ */
+ public char getChar(String key) {
+ unparcel();
+ return getChar(key, (char) 0);
+ }
+
+ /**
+ * Returns the value associated with the given key, or (char) 0 if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a char value
+ */
+ public char getChar(String key, char defaultValue) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return defaultValue;
+ }
+ try {
+ return (Character) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Character", defaultValue, e);
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or (short) 0 if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a short value
+ */
+ public short getShort(String key) {
+ unparcel();
+ return getShort(key, (short) 0);
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a short value
+ */
+ public short getShort(String key, short defaultValue) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return defaultValue;
+ }
+ try {
+ return (Short) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Short", defaultValue, e);
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or 0 if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return an int value
+ */
+ public int getInt(String key) {
+ unparcel();
+ return getInt(key, 0);
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return an int value
+ */
+ public int getInt(String key, int defaultValue) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return defaultValue;
+ }
+ try {
+ return (Integer) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Integer", defaultValue, e);
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or 0L if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a long value
+ */
+ public long getLong(String key) {
+ unparcel();
+ return getLong(key, 0L);
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a long value
+ */
+ public long getLong(String key, long defaultValue) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return defaultValue;
+ }
+ try {
+ return (Long) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Long", defaultValue, e);
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or 0.0f if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a float value
+ */
+ public float getFloat(String key) {
+ unparcel();
+ return getFloat(key, 0.0f);
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a float value
+ */
+ public float getFloat(String key, float defaultValue) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return defaultValue;
+ }
+ try {
+ return (Float) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Float", defaultValue, e);
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or 0.0 if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a double value
+ */
+ public double getDouble(String key) {
+ unparcel();
+ return getDouble(key, 0.0);
+ }
+
+ /**
+ * Returns the value associated with the given key, or defaultValue if
+ * no mapping of the desired type exists for the given key.
+ *
+ * @param key a String
+ * @return a double value
+ */
+ public double getDouble(String key, double defaultValue) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return defaultValue;
+ }
+ try {
+ return (Double) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Double", defaultValue, e);
+ return defaultValue;
+ }
+ }
+
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a String value, or null
+ */
+ public String getString(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (String) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "String", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a CharSequence value, or null
+ */
+ public CharSequence getCharSequence(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (CharSequence) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "CharSequence", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a Bundle value, or null
+ */
+ public Bundle getBundle(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (Bundle) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Bundle", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a Parcelable value, or null
+ */
+ public <T extends Parcelable> T getParcelable(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (T) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Parcelable", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a Parcelable[] value, or null
+ */
+ public Parcelable[] getParcelableArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (Parcelable[]) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Parcelable[]", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return an ArrayList<T> value, or null
+ */
+ public <T extends Parcelable> ArrayList<T> getParcelableArrayList(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (ArrayList<T>) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "ArrayList", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ *
+ * @return a SparseArray of T values, or null
+ */
+ public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (SparseArray<T>) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "SparseArray", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a Serializable value, or null
+ */
+ public Serializable getSerializable(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (Serializable) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Serializable", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return an ArrayList<String> value, or null
+ */
+ public ArrayList<Integer> getIntegerArrayList(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (ArrayList<Integer>) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "ArrayList<Integer>", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return an ArrayList<String> value, or null
+ */
+ public ArrayList<String> getStringArrayList(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (ArrayList<String>) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "ArrayList<String>", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a boolean[] value, or null
+ */
+ public boolean[] getBooleanArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (boolean[]) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "byte[]", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a byte[] value, or null
+ */
+ public byte[] getByteArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (byte[]) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "byte[]", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a short[] value, or null
+ */
+ public short[] getShortArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (short[]) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "short[]", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a char[] value, or null
+ */
+ public char[] getCharArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (char[]) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "char[]", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return an int[] value, or null
+ */
+ public int[] getIntArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (int[]) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "int[]", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a long[] value, or null
+ */
+ public long[] getLongArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (long[]) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "long[]", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a float[] value, or null
+ */
+ public float[] getFloatArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (float[]) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "float[]", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a double[] value, or null
+ */
+ public double[] getDoubleArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (double[]) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "double[]", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return a String[] value, or null
+ */
+ public String[] getStringArray(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (String[]) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "String[]", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
+ * @return an IBinder value, or null
+ *
+ * @deprecated
+ * @hide
+ */
+ @Deprecated
+ public IBinder getIBinder(String key) {
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (IBinder) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "IBinder", e);
+ return null;
+ }
+ }
+
+ public static final Parcelable.Creator<Bundle> CREATOR =
+ new Parcelable.Creator<Bundle>() {
+ public Bundle createFromParcel(Parcel in) {
+ return in.readBundle();
+ }
+
+ public Bundle[] newArray(int size) {
+ return new Bundle[size];
+ }
+ };
+
+ /**
+ * Report the nature of this Parcelable's contents
+ */
+ public int describeContents() {
+ int mask = 0;
+ if (hasFileDescriptors()) {
+ mask |= Parcelable.CONTENTS_FILE_DESCRIPTOR;
+ }
+ return mask;
+ }
+
+ /**
+ * Writes the Bundle contents to a Parcel, typically in order for
+ * it to be passed through an IBinder connection.
+ * @param parcel The parcel to copy this bundle to.
+ */
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeBundle(this);
+ }
+
+ /**
+ * Reads the Parcel contents into this Bundle, typically in order for
+ * it to be passed through an IBinder connection.
+ * @param parcel The parcel to overwrite this bundle from.
+ */
+ public void readFromParcel(Parcel parcel) {
+ mParcelledData = parcel;
+ mHasFds = mParcelledData.hasFileDescriptors();
+ mFdsKnown = true;
+ }
+
+ @Override
+ public synchronized String toString() {
+ if (mParcelledData != null) {
+ return "Bundle[mParcelledData.dataSize=" +
+ mParcelledData.dataSize() + "]";
+ }
+ return "Bundle[" + mMap.toString() + "]";
+ }
+}
diff --git a/core/java/android/os/ConditionVariable.java b/core/java/android/os/ConditionVariable.java
new file mode 100644
index 0000000..95a9259
--- /dev/null
+++ b/core/java/android/os/ConditionVariable.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * Class that implements the condition variable locking paradigm.
+ *
+ * <p>
+ * This differs from the built-in java.lang.Object wait() and notify()
+ * in that this class contains the condition to wait on itself. That means
+ * open(), close() and block() are sticky. If open() is called before block(),
+ * block() will not block, and instead return immediately.
+ *
+ * <p>
+ * This class uses itself is at the object to wait on, so if you wait()
+ * or notify() on a ConditionVariable, the results are undefined.
+ */
+public class ConditionVariable
+{
+ private volatile boolean mCondition;
+
+ /**
+ * Create the ConditionVariable in the default closed state.
+ */
+ public ConditionVariable()
+ {
+ mCondition = false;
+ }
+
+ /**
+ * Create the ConditionVariable with the given state.
+ *
+ * <p>
+ * Pass true for opened and false for closed.
+ */
+ public ConditionVariable(boolean state)
+ {
+ mCondition = state;
+ }
+
+ /**
+ * Open the condition, and release all threads that are blocked.
+ *
+ * <p>
+ * Any threads that later approach block() will not block unless close()
+ * is called.
+ */
+ public void open()
+ {
+ synchronized (this) {
+ boolean old = mCondition;
+ mCondition = true;
+ if (!old) {
+ this.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Reset the condition to the closed state.
+ *
+ * <p>
+ * Any threads that call block() will block until someone calls open.
+ */
+ public void close()
+ {
+ synchronized (this) {
+ mCondition = false;
+ }
+ }
+
+ /**
+ * Block the current thread until the condition is opened.
+ *
+ * <p>
+ * If the condition is already opened, return immediately.
+ */
+ public void block()
+ {
+ synchronized (this) {
+ while (!mCondition) {
+ try {
+ this.wait();
+ }
+ catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Block the current thread until the condition is opened or until
+ * timeout milliseconds have passed.
+ *
+ * <p>
+ * If the condition is already opened, return immediately.
+ *
+ * @param timeout the minimum time to wait in milliseconds.
+ *
+ * @return true if the condition was opened, false if the call returns
+ * because of the timeout.
+ */
+ public boolean block(long timeout)
+ {
+ // Object.wait(0) means wait forever, to mimic this, we just
+ // call the other block() method in that case. It simplifies
+ // this code for the common case.
+ if (timeout != 0) {
+ synchronized (this) {
+ long now = System.currentTimeMillis();
+ long end = now + timeout;
+ while (!mCondition && now < end) {
+ try {
+ this.wait(end-now);
+ }
+ catch (InterruptedException e) {
+ }
+ now = System.currentTimeMillis();
+ }
+ return mCondition;
+ }
+ } else {
+ this.block();
+ return true;
+ }
+ }
+}
diff --git a/core/java/android/os/CountDownTimer.java b/core/java/android/os/CountDownTimer.java
new file mode 100644
index 0000000..0c5c615
--- /dev/null
+++ b/core/java/android/os/CountDownTimer.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2008 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 android.os;
+
+import android.util.Log;
+
+/**
+ * Schedule a countdown until a time in the future, with
+ * regular notifications on intervals along the way.
+ *
+ * Example of showing a 30 second countdown in a text field:
+ *
+ * <pre class="prettyprint">
+ * new CountdownTimer(30000, 1000) {
+ *
+ * public void onTick(long millisUntilFinished) {
+ * mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
+ * }
+ *
+ * public void onFinish() {
+ * mTextField.setText("done!");
+ * }
+ * }.start();
+ * </pre>
+ *
+ * The calls to {@link #onTick(long)} are synchronized to this object so that
+ * one call to {@link #onTick(long)} won't ever occur before the previous
+ * callback is complete. This is only relevant when the implementation of
+ * {@link #onTick(long)} takes an amount of time to execute that is significant
+ * compared to the countdown interval.
+ */
+public abstract class CountDownTimer {
+
+ /**
+ * Millis since epoch when alarm should stop.
+ */
+ private final long mMillisInFuture;
+
+ /**
+ * The interval in millis that the user receives callbacks
+ */
+ private final long mCountdownInterval;
+
+ private long mStopTimeInFuture;
+
+ /**
+ * @param millisInFuture The number of millis in the future from the call
+ * to {@link #start()} until the countdown is done and {@link #onFinish()}
+ * is called.
+ * @param countDownInterval The interval along the way to receive
+ * {@link #onTick(long)} callbacks.
+ */
+ public CountDownTimer(long millisInFuture, long countDownInterval) {
+ mMillisInFuture = millisInFuture;
+ mCountdownInterval = countDownInterval;
+ }
+
+ /**
+ * Cancel the countdown.
+ */
+ public final void cancel() {
+ mHandler.removeMessages(MSG);
+ }
+
+ /**
+ * Start the countdown.
+ */
+ public synchronized final CountDownTimer start() {
+ if (mMillisInFuture <= 0) {
+ onFinish();
+ return this;
+ }
+ mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
+ mHandler.sendMessage(mHandler.obtainMessage(MSG));
+ return this;
+ }
+
+
+ /**
+ * Callback fired on regular interval.
+ * @param millisUntilFinished The amount of time until finished.
+ */
+ public abstract void onTick(long millisUntilFinished);
+
+ /**
+ * Callback fired when the time is up.
+ */
+ public abstract void onFinish();
+
+
+ private static final int MSG = 1;
+
+
+ // handles counting down
+ private Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+
+ synchronized (CountDownTimer.this) {
+ final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
+
+ if (millisLeft <= 0) {
+ onFinish();
+ } else if (millisLeft < mCountdownInterval) {
+ // no tick, just delay until done
+ sendMessageDelayed(obtainMessage(MSG), millisLeft);
+ } else {
+ long lastTickStart = SystemClock.elapsedRealtime();
+ onTick(millisLeft);
+
+ // take into account user's onTick taking time to execute
+ long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
+
+ // special case: user's onTick took more than interval to
+ // complete, skip to next interval
+ while (delay < 0) delay += mCountdownInterval;
+
+ sendMessageDelayed(obtainMessage(MSG), delay);
+ }
+ }
+ }
+ };
+}
diff --git a/core/java/android/os/DeadObjectException.java b/core/java/android/os/DeadObjectException.java
new file mode 100644
index 0000000..94c5387
--- /dev/null
+++ b/core/java/android/os/DeadObjectException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+import android.os.RemoteException;
+
+/**
+ * The object you are calling has died, because its hosting process
+ * no longer exists.
+ */
+public class DeadObjectException extends RemoteException {
+ public DeadObjectException() {
+ super();
+ }
+}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
new file mode 100644
index 0000000..950bb09
--- /dev/null
+++ b/core/java/android/os/Debug.java
@@ -0,0 +1,717 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
+import dalvik.bytecode.Opcodes;
+import dalvik.system.VMDebug;
+
+
+/**
+ * Provides various debugging functions for Android applications, including
+ * tracing and allocation counts.
+ * <p><strong>Logging Trace Files</strong></p>
+ * <p>Debug can create log files that give details about an application, such as
+ * a call stack and start/stop times for any running methods. See <a
+href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for
+ * information about reading trace files. To start logging trace files, call one
+ * of the startMethodTracing() methods. To stop tracing, call
+ * {@link #stopMethodTracing()}.
+ */
+public final class Debug
+{
+ /**
+ * Flags for startMethodTracing(). These can be ORed together.
+ *
+ * TRACE_COUNT_ALLOCS adds the results from startAllocCounting to the
+ * trace key file.
+ */
+ public static final int TRACE_COUNT_ALLOCS = VMDebug.TRACE_COUNT_ALLOCS;
+
+ /**
+ * Flags for printLoadedClasses(). Default behavior is to only show
+ * the class name.
+ */
+ public static final int SHOW_FULL_DETAIL = 1;
+ public static final int SHOW_CLASSLOADER = (1 << 1);
+ public static final int SHOW_INITIALIZED = (1 << 2);
+
+ // set/cleared by waitForDebugger()
+ private static volatile boolean mWaiting = false;
+
+ private Debug() {}
+
+ /*
+ * How long to wait for the debugger to finish sending requests. I've
+ * seen this hit 800msec on the device while waiting for a response
+ * to travel over USB and get processed, so we take that and add
+ * half a second.
+ */
+ private static final int MIN_DEBUGGER_IDLE = 1300; // msec
+
+ /* how long to sleep when polling for activity */
+ private static final int SPIN_DELAY = 200; // msec
+
+ /**
+ * Default trace file path and file
+ */
+ private static final String DEFAULT_TRACE_PATH_PREFIX = "/sdcard/";
+ private static final String DEFAULT_TRACE_BODY = "dmtrace";
+ private static final String DEFAULT_TRACE_EXTENSION = ".trace";
+ private static final String DEFAULT_TRACE_FILE_PATH =
+ DEFAULT_TRACE_PATH_PREFIX + DEFAULT_TRACE_BODY
+ + DEFAULT_TRACE_EXTENSION;
+
+
+ /**
+ * This class is used to retrieved various statistics about the memory mappings for this
+ * process. The returns info broken down by dalvik, native, and other. All results are in kB.
+ */
+ public static class MemoryInfo {
+ /** The proportional set size for dalvik. */
+ public int dalvikPss;
+ /** The private dirty pages used by dalvik. */
+ public int dalvikPrivateDirty;
+ /** The shared dirty pages used by dalvik. */
+ public int dalvikSharedDirty;
+
+ /** The proportional set size for the native heap. */
+ public int nativePss;
+ /** The private dirty pages used by the native heap. */
+ public int nativePrivateDirty;
+ /** The shared dirty pages used by the native heap. */
+ public int nativeSharedDirty;
+
+ /** The proportional set size for everything else. */
+ public int otherPss;
+ /** The private dirty pages used by everything else. */
+ public int otherPrivateDirty;
+ /** The shared dirty pages used by everything else. */
+ public int otherSharedDirty;
+ }
+
+
+ /**
+ * Wait until a debugger attaches. As soon as the debugger attaches,
+ * this returns, so you will need to place a breakpoint after the
+ * waitForDebugger() call if you want to start tracing immediately.
+ */
+ public static void waitForDebugger() {
+ if (!VMDebug.isDebuggingEnabled()) {
+ //System.out.println("debugging not enabled, not waiting");
+ return;
+ }
+ if (isDebuggerConnected())
+ return;
+
+ // if DDMS is listening, inform them of our plight
+ System.out.println("Sending WAIT chunk");
+ byte[] data = new byte[] { 0 }; // 0 == "waiting for debugger"
+ Chunk waitChunk = new Chunk(ChunkHandler.type("WAIT"), data, 0, 1);
+ DdmServer.sendChunk(waitChunk);
+
+ mWaiting = true;
+ while (!isDebuggerConnected()) {
+ try { Thread.sleep(SPIN_DELAY); }
+ catch (InterruptedException ie) {}
+ }
+ mWaiting = false;
+
+ System.out.println("Debugger has connected");
+
+ /*
+ * There is no "ready to go" signal from the debugger, and we're
+ * not allowed to suspend ourselves -- the debugger expects us to
+ * be running happily, and gets confused if we aren't. We need to
+ * allow the debugger a chance to set breakpoints before we start
+ * running again.
+ *
+ * Sit and spin until the debugger has been idle for a short while.
+ */
+ while (true) {
+ long delta = VMDebug.lastDebuggerActivity();
+ if (delta < 0) {
+ System.out.println("debugger detached?");
+ break;
+ }
+
+ if (delta < MIN_DEBUGGER_IDLE) {
+ System.out.println("waiting for debugger to settle...");
+ try { Thread.sleep(SPIN_DELAY); }
+ catch (InterruptedException ie) {}
+ } else {
+ System.out.println("debugger has settled (" + delta + ")");
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns "true" if one or more threads is waiting for a debugger
+ * to attach.
+ */
+ public static boolean waitingForDebugger() {
+ return mWaiting;
+ }
+
+ /**
+ * Determine if a debugger is currently attached.
+ */
+ public static boolean isDebuggerConnected() {
+ return VMDebug.isDebuggerConnected();
+ }
+
+ /**
+ * Change the JDWP port.
+ *
+ * @deprecated no longer needed or useful
+ */
+ @Deprecated
+ public static void changeDebugPort(int port) {}
+
+ /**
+ * This is the pathname to the sysfs file that enables and disables
+ * tracing on the qemu emulator.
+ */
+ private static final String SYSFS_QEMU_TRACE_STATE = "/sys/qemu_trace/state";
+
+ /**
+ * Enable qemu tracing. For this to work requires running everything inside
+ * the qemu emulator; otherwise, this method will have no effect. The trace
+ * file is specified on the command line when the emulator is started. For
+ * example, the following command line <br />
+ * <code>emulator -trace foo</code><br />
+ * will start running the emulator and create a trace file named "foo". This
+ * method simply enables writing the trace records to the trace file.
+ *
+ * <p>
+ * The main differences between this and {@link #startMethodTracing()} are
+ * that tracing in the qemu emulator traces every cpu instruction of every
+ * process, including kernel code, so we have more complete information,
+ * including all context switches. We can also get more detailed information
+ * such as cache misses. The sequence of calls is determined by
+ * post-processing the instruction trace. The qemu tracing is also done
+ * without modifying the application or perturbing the timing of calls
+ * because no instrumentation is added to the application being traced.
+ * </p>
+ *
+ * <p>
+ * One limitation of using this method compared to using
+ * {@link #startMethodTracing()} on the real device is that the emulator
+ * does not model all of the real hardware effects such as memory and
+ * bus contention. The emulator also has a simple cache model and cannot
+ * capture all the complexities of a real cache.
+ * </p>
+ */
+ public static void startNativeTracing() {
+ // Open the sysfs file for writing and write "1" to it.
+ PrintWriter outStream = null;
+ try {
+ FileOutputStream fos = new FileOutputStream(SYSFS_QEMU_TRACE_STATE);
+ outStream = new PrintWriter(new OutputStreamWriter(fos));
+ outStream.println("1");
+ } catch (Exception e) {
+ } finally {
+ if (outStream != null)
+ outStream.close();
+ }
+
+ VMDebug.startEmulatorTracing();
+ }
+
+ /**
+ * Stop qemu tracing. See {@link #startNativeTracing()} to start tracing.
+ *
+ * <p>Tracing can be started and stopped as many times as desired. When
+ * the qemu emulator itself is stopped then the buffered trace records
+ * are flushed and written to the trace file. In fact, it is not necessary
+ * to call this method at all; simply killing qemu is sufficient. But
+ * starting and stopping a trace is useful for examining a specific
+ * region of code.</p>
+ */
+ public static void stopNativeTracing() {
+ VMDebug.stopEmulatorTracing();
+
+ // Open the sysfs file for writing and write "0" to it.
+ PrintWriter outStream = null;
+ try {
+ FileOutputStream fos = new FileOutputStream(SYSFS_QEMU_TRACE_STATE);
+ outStream = new PrintWriter(new OutputStreamWriter(fos));
+ outStream.println("0");
+ } catch (Exception e) {
+ // We could print an error message here but we probably want
+ // to quietly ignore errors if we are not running in the emulator.
+ } finally {
+ if (outStream != null)
+ outStream.close();
+ }
+ }
+
+ /**
+ * Enable "emulator traces", in which information about the current
+ * method is made available to the "emulator -trace" feature. There
+ * is no corresponding "disable" call -- this is intended for use by
+ * the framework when tracing should be turned on and left that way, so
+ * that traces captured with F9/F10 will include the necessary data.
+ *
+ * This puts the VM into "profile" mode, which has performance
+ * consequences.
+ *
+ * To temporarily enable tracing, use {@link #startNativeTracing()}.
+ */
+ public static void enableEmulatorTraceOutput() {
+ VMDebug.startEmulatorTracing();
+ }
+
+ /**
+ * Start method tracing with default log name and buffer size. See <a
+href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for
+ * information about reading these files. Call stopMethodTracing() to stop
+ * tracing.
+ */
+ public static void startMethodTracing() {
+ VMDebug.startMethodTracing(DEFAULT_TRACE_FILE_PATH, 0, 0);
+ }
+
+ /**
+ * Start method tracing, specifying the trace log file name. The trace
+ * file will be put under "/sdcard" unless an absolute path is given.
+ * See <a
+ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for
+ * information about reading trace files.
+ *
+ * @param traceName Name for the trace log file to create.
+ * If no name argument is given, this value defaults to "/sdcard/dmtrace.trace".
+ * If the files already exist, they will be truncated.
+ * If the trace file given does not end in ".trace", it will be appended for you.
+ */
+ public static void startMethodTracing(String traceName) {
+ startMethodTracing(traceName, 0, 0);
+ }
+
+ /**
+ * Start method tracing, specifying the trace log file name and the
+ * buffer size. The trace files will be put under "/sdcard" unless an
+ * absolute path is given. See <a
+ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for
+ * information about reading trace files.
+ * @param traceName Name for the trace log file to create.
+ * If no name argument is given, this value defaults to "/sdcard/dmtrace.trace".
+ * If the files already exist, they will be truncated.
+ * If the trace file given does not end in ".trace", it will be appended for you.
+ *
+ * @param bufferSize The maximum amount of trace data we gather. If not given, it defaults to 8MB.
+ */
+ public static void startMethodTracing(String traceName, int bufferSize) {
+ startMethodTracing(traceName, bufferSize, 0);
+ }
+
+ /**
+ * Start method tracing, specifying the trace log file name and the
+ * buffer size. The trace files will be put under "/sdcard" unless an
+ * absolute path is given. See <a
+ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for
+ * information about reading trace files.
+ *
+ * <p>
+ * When method tracing is enabled, the VM will run more slowly than
+ * usual, so the timings from the trace files should only be considered
+ * in relative terms (e.g. was run #1 faster than run #2). The times
+ * for native methods will not change, so don't try to use this to
+ * compare the performance of interpreted and native implementations of the
+ * same method. As an alternative, consider using "native" tracing
+ * in the emulator via {@link #startNativeTracing()}.
+ * </p>
+ *
+ * @param traceName Name for the trace log file to create.
+ * If no name argument is given, this value defaults to "/sdcard/dmtrace.trace".
+ * If the files already exist, they will be truncated.
+ * If the trace file given does not end in ".trace", it will be appended for you.
+ * @param bufferSize The maximum amount of trace data we gather. If not given, it defaults to 8MB.
+ */
+ public static void startMethodTracing(String traceName, int bufferSize,
+ int flags) {
+
+ String pathName = traceName;
+ if (pathName.charAt(0) != '/')
+ pathName = DEFAULT_TRACE_PATH_PREFIX + pathName;
+ if (!pathName.endsWith(DEFAULT_TRACE_EXTENSION))
+ pathName = pathName + DEFAULT_TRACE_EXTENSION;
+
+ VMDebug.startMethodTracing(pathName, bufferSize, flags);
+ }
+
+ /**
+ * Stop method tracing.
+ */
+ public static void stopMethodTracing() {
+ VMDebug.stopMethodTracing();
+ }
+
+ /**
+ * Get an indication of thread CPU usage. The value returned
+ * indicates the amount of time that the current thread has spent
+ * executing code or waiting for certain types of I/O.
+ *
+ * The time is expressed in nanoseconds, and is only meaningful
+ * when compared to the result from an earlier call. Note that
+ * nanosecond resolution does not imply nanosecond accuracy.
+ *
+ * On system which don't support this operation, the call returns -1.
+ */
+ public static long threadCpuTimeNanos() {
+ return VMDebug.threadCpuTimeNanos();
+ }
+
+ /**
+ * Count the number and aggregate size of memory allocations between
+ * two points.
+ *
+ * The "start" function resets the counts and enables counting. The
+ * "stop" function disables the counting so that the analysis code
+ * doesn't cause additional allocations. The "get" function returns
+ * the specified value.
+ *
+ * Counts are kept for the system as a whole and for each thread.
+ * The per-thread counts for threads other than the current thread
+ * are not cleared by the "reset" or "start" calls.
+ */
+ public static void startAllocCounting() {
+ VMDebug.startAllocCounting();
+ }
+ public static void stopAllocCounting() {
+ VMDebug.stopAllocCounting();
+ }
+
+ public static int getGlobalAllocCount() {
+ return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_OBJECTS);
+ }
+ public static int getGlobalAllocSize() {
+ return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_BYTES);
+ }
+ public static int getGlobalFreedCount() {
+ return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_FREED_OBJECTS);
+ }
+ public static int getGlobalFreedSize() {
+ return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES);
+ }
+ public static int getGlobalExternalAllocCount() {
+ return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_OBJECTS);
+ }
+ public static int getGlobalExternalAllocSize() {
+ return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_BYTES);
+ }
+ public static int getGlobalExternalFreedCount() {
+ return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_EXT_FREED_OBJECTS);
+ }
+ public static int getGlobalExternalFreedSize() {
+ return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_EXT_FREED_BYTES);
+ }
+ public static int getGlobalGcInvocationCount() {
+ return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_GC_INVOCATIONS);
+ }
+ public static int getThreadAllocCount() {
+ return VMDebug.getAllocCount(VMDebug.KIND_THREAD_ALLOCATED_OBJECTS);
+ }
+ public static int getThreadAllocSize() {
+ return VMDebug.getAllocCount(VMDebug.KIND_THREAD_ALLOCATED_BYTES);
+ }
+ public static int getThreadExternalAllocCount() {
+ return VMDebug.getAllocCount(VMDebug.KIND_THREAD_EXT_ALLOCATED_OBJECTS);
+ }
+ public static int getThreadExternalAllocSize() {
+ return VMDebug.getAllocCount(VMDebug.KIND_THREAD_EXT_ALLOCATED_BYTES);
+ }
+ public static int getThreadGcInvocationCount() {
+ return VMDebug.getAllocCount(VMDebug.KIND_THREAD_GC_INVOCATIONS);
+ }
+
+ public static void resetGlobalAllocCount() {
+ VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_OBJECTS);
+ }
+ public static void resetGlobalAllocSize() {
+ VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_BYTES);
+ }
+ public static void resetGlobalFreedCount() {
+ VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_FREED_OBJECTS);
+ }
+ public static void resetGlobalFreedSize() {
+ VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES);
+ }
+ public static void resetGlobalExternalAllocCount() {
+ VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_OBJECTS);
+ }
+ public static void resetGlobalExternalAllocSize() {
+ VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_BYTES);
+ }
+ public static void resetGlobalExternalFreedCount() {
+ VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_EXT_FREED_OBJECTS);
+ }
+ public static void resetGlobalExternalFreedSize() {
+ VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_EXT_FREED_BYTES);
+ }
+ public static void resetGlobalGcInvocationCount() {
+ VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_GC_INVOCATIONS);
+ }
+ public static void resetThreadAllocCount() {
+ VMDebug.resetAllocCount(VMDebug.KIND_THREAD_ALLOCATED_OBJECTS);
+ }
+ public static void resetThreadAllocSize() {
+ VMDebug.resetAllocCount(VMDebug.KIND_THREAD_ALLOCATED_BYTES);
+ }
+ public static void resetThreadExternalAllocCount() {
+ VMDebug.resetAllocCount(VMDebug.KIND_THREAD_EXT_ALLOCATED_OBJECTS);
+ }
+ public static void resetThreadExternalAllocSize() {
+ VMDebug.resetAllocCount(VMDebug.KIND_THREAD_EXT_ALLOCATED_BYTES);
+ }
+ public static void resetThreadGcInvocationCount() {
+ VMDebug.resetAllocCount(VMDebug.KIND_THREAD_GC_INVOCATIONS);
+ }
+ public static void resetAllCounts() {
+ VMDebug.resetAllocCount(VMDebug.KIND_ALL_COUNTS);
+ }
+
+ /**
+ * Returns the size of the native heap.
+ * @return The size of the native heap in bytes.
+ */
+ public static native long getNativeHeapSize();
+
+ /**
+ * Returns the amount of allocated memory in the native heap.
+ * @return The allocated size in bytes.
+ */
+ public static native long getNativeHeapAllocatedSize();
+
+ /**
+ * Returns the amount of free memory in the native heap.
+ * @return The freed size in bytes.
+ */
+ public static native long getNativeHeapFreeSize();
+
+ /**
+ * Retrieves information about this processes memory usages. This information is broken down by
+ * how much is in use by dalivk, the native heap, and everything else.
+ */
+ public static native void getMemoryInfo(MemoryInfo memoryInfo);
+
+ /**
+ * Establish an object allocation limit in the current thread. Useful
+ * for catching regressions in code that is expected to operate
+ * without causing any allocations.
+ *
+ * Pass in the maximum number of allowed allocations. Use -1 to disable
+ * the limit. Returns the previous limit.
+ *
+ * The preferred way to use this is:
+ *
+ * int prevLimit = -1;
+ * try {
+ * prevLimit = Debug.setAllocationLimit(0);
+ * ... do stuff that's not expected to allocate memory ...
+ * } finally {
+ * Debug.setAllocationLimit(prevLimit);
+ * }
+ *
+ * This allows limits to be nested. The try/finally ensures that the
+ * limit is reset if something fails.
+ *
+ * Exceeding the limit causes a dalvik.system.AllocationLimitError to
+ * be thrown from a memory allocation call. The limit is reset to -1
+ * when this happens.
+ *
+ * The feature may be disabled in the VM configuration. If so, this
+ * call has no effect, and always returns -1.
+ */
+ public static int setAllocationLimit(int limit) {
+ return VMDebug.setAllocationLimit(limit);
+ }
+
+ /**
+ * Establish a global object allocation limit. This is similar to
+ * {@link #setAllocationLimit(int)} but applies to all threads in
+ * the VM. It will coexist peacefully with per-thread limits.
+ *
+ * [ The value of "limit" is currently restricted to 0 (no allocations
+ * allowed) or -1 (no global limit). This may be changed in a future
+ * release. ]
+ */
+ public static int setGlobalAllocationLimit(int limit) {
+ if (limit != 0 && limit != -1)
+ throw new IllegalArgumentException("limit must be 0 or -1");
+ return VMDebug.setGlobalAllocationLimit(limit);
+ }
+
+ /**
+ * Dump a list of all currently loaded class to the log file.
+ *
+ * @param flags See constants above.
+ */
+ public static void printLoadedClasses(int flags) {
+ VMDebug.printLoadedClasses(flags);
+ }
+
+ /**
+ * Get the number of loaded classes.
+ * @return the number of loaded classes.
+ */
+ public static int getLoadedClassCount() {
+ return VMDebug.getLoadedClassCount();
+ }
+
+ /**
+ * Dump "hprof" data to the specified file. This will cause a GC.
+ *
+ * @param fileName Full pathname of output file (e.g. "/sdcard/dump.hprof").
+ * @throws UnsupportedOperationException if the VM was built without
+ * HPROF support.
+ * @throws IOException if an error occurs while opening or writing files.
+ */
+ public static void dumpHprofData(String fileName) throws IOException {
+ VMDebug.dumpHprofData(fileName);
+ }
+
+ /**
+ * Returns the number of sent transactions from this process.
+ * @return The number of sent transactions or -1 if it could not read t.
+ */
+ public static native int getBinderSentTransactions();
+
+ /**
+ * Returns the number of received transactions from the binder driver.
+ * @return The number of received transactions or -1 if it could not read the stats.
+ */
+ public static native int getBinderReceivedTransactions();
+
+ /**
+ * Returns the number of active local Binder objects that exist in the
+ * current process.
+ */
+ public static final native int getBinderLocalObjectCount();
+
+ /**
+ * Returns the number of references to remote proxy Binder objects that
+ * exist in the current process.
+ */
+ public static final native int getBinderProxyObjectCount();
+
+ /**
+ * Returns the number of death notification links to Binder objects that
+ * exist in the current process.
+ */
+ public static final native int getBinderDeathObjectCount();
+
+ /**
+ * API for gathering and querying instruction counts.
+ *
+ * Example usage:
+ * Debug.InstructionCount icount = new Debug.InstructionCount();
+ * icount.resetAndStart();
+ * [... do lots of stuff ...]
+ * if (icount.collect()) {
+ * System.out.println("Total instructions executed: "
+ * + icount.globalTotal());
+ * System.out.println("Method invocations: "
+ * + icount.globalMethodInvocations());
+ * }
+ */
+ public static class InstructionCount {
+ private static final int NUM_INSTR = 256;
+
+ private int[] mCounts;
+
+ public InstructionCount() {
+ mCounts = new int[NUM_INSTR];
+ }
+
+ /**
+ * Reset counters and ensure counts are running. Counts may
+ * have already been running.
+ *
+ * @return true if counting was started
+ */
+ public boolean resetAndStart() {
+ try {
+ VMDebug.startInstructionCounting();
+ VMDebug.resetInstructionCount();
+ } catch (UnsupportedOperationException uoe) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Collect instruction counts. May or may not stop the
+ * counting process.
+ */
+ public boolean collect() {
+ try {
+ VMDebug.stopInstructionCounting();
+ VMDebug.getInstructionCount(mCounts);
+ } catch (UnsupportedOperationException uoe) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return the total number of instructions executed globally (i.e. in
+ * all threads).
+ */
+ public int globalTotal() {
+ int count = 0;
+ for (int i = 0; i < NUM_INSTR; i++)
+ count += mCounts[i];
+ return count;
+ }
+
+ /**
+ * Return the total number of method-invocation instructions
+ * executed globally.
+ */
+ public int globalMethodInvocations() {
+ int count = 0;
+
+ //count += mCounts[Opcodes.OP_EXECUTE_INLINE];
+ count += mCounts[Opcodes.OP_INVOKE_VIRTUAL];
+ count += mCounts[Opcodes.OP_INVOKE_SUPER];
+ count += mCounts[Opcodes.OP_INVOKE_DIRECT];
+ count += mCounts[Opcodes.OP_INVOKE_STATIC];
+ count += mCounts[Opcodes.OP_INVOKE_INTERFACE];
+ count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_RANGE];
+ count += mCounts[Opcodes.OP_INVOKE_SUPER_RANGE];
+ count += mCounts[Opcodes.OP_INVOKE_DIRECT_RANGE];
+ count += mCounts[Opcodes.OP_INVOKE_STATIC_RANGE];
+ count += mCounts[Opcodes.OP_INVOKE_INTERFACE_RANGE];
+ //count += mCounts[Opcodes.OP_INVOKE_DIRECT_EMPTY];
+ count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_QUICK];
+ count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_QUICK_RANGE];
+ count += mCounts[Opcodes.OP_INVOKE_SUPER_QUICK];
+ count += mCounts[Opcodes.OP_INVOKE_SUPER_QUICK_RANGE];
+ return count;
+ }
+ };
+}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
new file mode 100644
index 0000000..f761e8e
--- /dev/null
+++ b/core/java/android/os/Environment.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.io.File;
+
+/**
+ * Provides access to environment variables.
+ */
+public class Environment {
+
+ private static final File ROOT_DIRECTORY
+ = getDirectory("ANDROID_ROOT", "/system");
+
+ /**
+ * Gets the Android root directory.
+ */
+ public static File getRootDirectory() {
+ return ROOT_DIRECTORY;
+ }
+
+ private static final File DATA_DIRECTORY
+ = getDirectory("ANDROID_DATA", "/data");
+
+ private static final File EXTERNAL_STORAGE_DIRECTORY
+ = getDirectory("EXTERNAL_STORAGE", "/sdcard");
+
+ private static final File DOWNLOAD_CACHE_DIRECTORY
+ = getDirectory("DOWNLOAD_CACHE", "/cache");
+
+ /**
+ * Gets the Android data directory.
+ */
+ public static File getDataDirectory() {
+ return DATA_DIRECTORY;
+ }
+
+ /**
+ * Gets the Android external storage directory.
+ */
+ public static File getExternalStorageDirectory() {
+ return EXTERNAL_STORAGE_DIRECTORY;
+ }
+
+ /**
+ * Gets the Android Download/Cache content directory.
+ */
+ public static File getDownloadCacheDirectory() {
+ return DOWNLOAD_CACHE_DIRECTORY;
+ }
+
+ /**
+ * getExternalStorageState() returns MEDIA_REMOVED if the media is not present.
+ */
+ public static final String MEDIA_REMOVED = "removed";
+
+ /**
+ * getExternalStorageState() returns MEDIA_UNMOUNTED if the media is present
+ * but not mounted.
+ */
+ public static final String MEDIA_UNMOUNTED = "unmounted";
+
+ /**
+ * getExternalStorageState() returns MEDIA_CHECKING if the media is present
+ * and being disk-checked
+ */
+ public static final String MEDIA_CHECKING = "checking";
+
+ /**
+ * getExternalStorageState() returns MEDIA_NOFS if the media is present
+ * but is blank or is using an unsupported filesystem
+ */
+ public static final String MEDIA_NOFS = "nofs";
+
+ /**
+ * getExternalStorageState() returns MEDIA_MOUNTED if the media is present
+ * and mounted at its mount point with read/write access.
+ */
+ public static final String MEDIA_MOUNTED = "mounted";
+
+ /**
+ * getExternalStorageState() returns MEDIA_MOUNTED_READ_ONLY if the media is present
+ * and mounted at its mount point with read only access.
+ */
+ public static final String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
+
+ /**
+ * getExternalStorageState() returns MEDIA_SHARED if the media is present
+ * not mounted, and shared via USB mass storage.
+ */
+ public static final String MEDIA_SHARED = "shared";
+
+ /**
+ * getExternalStorageState() returns MEDIA_BAD_REMOVAL if the media was
+ * removed before it was unmounted.
+ */
+ public static final String MEDIA_BAD_REMOVAL = "bad_removal";
+
+ /**
+ * getExternalStorageState() returns MEDIA_UNMOUNTABLE if the media is present
+ * but cannot be mounted. Typically this happens if the file system on the
+ * media is corrupted.
+ */
+ public static final String MEDIA_UNMOUNTABLE = "unmountable";
+
+ /**
+ * Gets the current state of the external storage device.
+ */
+ public static String getExternalStorageState() {
+ return SystemProperties.get("EXTERNAL_STORAGE_STATE", MEDIA_REMOVED);
+ }
+
+ static File getDirectory(String variableName, String defaultPath) {
+ String path = System.getenv(variableName);
+ return path == null ? new File(defaultPath) : new File(path);
+ }
+}
diff --git a/core/java/android/os/Exec.java b/core/java/android/os/Exec.java
new file mode 100644
index 0000000..a50d5fe
--- /dev/null
+++ b/core/java/android/os/Exec.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.io.FileDescriptor;
+
+/**
+ * @hide
+ * Tools for executing commands. Not for public consumption.
+ */
+
+public class Exec
+{
+ /**
+ * @param cmd The command to execute
+ * @param arg0 The first argument to the command, may be null
+ * @param arg1 the second argument to the command, may be null
+ * @return the file descriptor of the started process.
+ *
+ */
+ public static FileDescriptor createSubprocess(
+ String cmd, String arg0, String arg1) {
+ return createSubprocess(cmd, arg0, arg1, null);
+ }
+
+ /**
+ * @param cmd The command to execute
+ * @param arg0 The first argument to the command, may be null
+ * @param arg1 the second argument to the command, may be null
+ * @param processId A one-element array to which the process ID of the
+ * started process will be written.
+ * @return the file descriptor of the started process.
+ *
+ */
+ public static native FileDescriptor createSubprocess(
+ String cmd, String arg0, String arg1, int[] processId);
+
+ public static native void setPtyWindowSize(FileDescriptor fd,
+ int row, int col, int xpixel, int ypixel);
+ /**
+ * Causes the calling thread to wait for the process associated with the
+ * receiver to finish executing.
+ *
+ * @return The exit value of the Process being waited on
+ *
+ */
+ public static native int waitFor(int processId);
+}
+
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
new file mode 100644
index 0000000..d9804ea
--- /dev/null
+++ b/core/java/android/os/FileObserver.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.util.Log;
+
+import com.android.internal.os.RuntimeInit;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public abstract class FileObserver {
+ public static final int ACCESS = 0x00000001; /* File was accessed */
+ public static final int MODIFY = 0x00000002; /* File was modified */
+ public static final int ATTRIB = 0x00000004; /* Metadata changed */
+ public static final int CLOSE_WRITE = 0x00000008; /* Writtable file was closed */
+ public static final int CLOSE_NOWRITE = 0x00000010; /* Unwrittable file closed */
+ public static final int OPEN = 0x00000020; /* File was opened */
+ public static final int MOVED_FROM = 0x00000040; /* File was moved from X */
+ public static final int MOVED_TO = 0x00000080; /* File was moved to Y */
+ public static final int CREATE = 0x00000100; /* Subfile was created */
+ public static final int DELETE = 0x00000200; /* Subfile was deleted */
+ public static final int DELETE_SELF = 0x00000400; /* Self was deleted */
+ public static final int MOVE_SELF = 0x00000800; /* Self was moved */
+ public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE
+ | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
+ | DELETE_SELF | MOVE_SELF;
+
+ private static final String LOG_TAG = "FileObserver";
+
+ private static class ObserverThread extends Thread {
+ private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
+ private int m_fd;
+
+ public ObserverThread() {
+ super("FileObserver");
+ m_fd = init();
+ }
+
+ public void run() {
+ observe(m_fd);
+ }
+
+ public int startWatching(String path, int mask, FileObserver observer) {
+ int wfd = startWatching(m_fd, path, mask);
+
+ Integer i = new Integer(wfd);
+ if (wfd >= 0) {
+ synchronized (m_observers) {
+ m_observers.put(i, new WeakReference(observer));
+ }
+ }
+
+ return i;
+ }
+
+ public void stopWatching(int descriptor) {
+ stopWatching(m_fd, descriptor);
+ }
+
+ public void onEvent(int wfd, int mask, String path) {
+ // look up our observer, fixing up the map if necessary...
+ FileObserver observer;
+
+ synchronized (m_observers) {
+ WeakReference weak = m_observers.get(wfd);
+ observer = (FileObserver) weak.get();
+ if (observer == null) {
+ m_observers.remove(wfd);
+ }
+ }
+
+ // ...then call out to the observer without the sync lock held
+ if (observer != null) {
+ try {
+ observer.onEvent(mask, path);
+ } catch (Throwable throwable) {
+ Log.e(LOG_TAG, "Unhandled throwable " + throwable.toString() +
+ " (returned by observer " + observer + ")", throwable);
+ RuntimeInit.crash("FileObserver", throwable);
+ }
+ }
+ }
+
+ private native int init();
+ private native void observe(int fd);
+ private native int startWatching(int fd, String path, int mask);
+ private native void stopWatching(int fd, int wfd);
+ }
+
+ private static ObserverThread s_observerThread;
+
+ static {
+ s_observerThread = new ObserverThread();
+ s_observerThread.start();
+ }
+
+ // instance
+ private String m_path;
+ private Integer m_descriptor;
+ private int m_mask;
+
+ public FileObserver(String path) {
+ this(path, ALL_EVENTS);
+ }
+
+ public FileObserver(String path, int mask) {
+ m_path = path;
+ m_mask = mask;
+ m_descriptor = -1;
+ }
+
+ protected void finalize() {
+ stopWatching();
+ }
+
+ public void startWatching() {
+ if (m_descriptor < 0) {
+ m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
+ }
+ }
+
+ public void stopWatching() {
+ if (m_descriptor >= 0) {
+ s_observerThread.stopWatching(m_descriptor);
+ m_descriptor = -1;
+ }
+ }
+
+ public abstract void onEvent(int event, String path);
+}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
new file mode 100644
index 0000000..51dfb5b
--- /dev/null
+++ b/core/java/android/os/FileUtils.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.regex.Pattern;
+
+
+/**
+ * Tools for managing files. Not for public consumption.
+ * @hide
+ */
+public class FileUtils
+{
+ public static final int S_IRWXU = 00700;
+ public static final int S_IRUSR = 00400;
+ public static final int S_IWUSR = 00200;
+ public static final int S_IXUSR = 00100;
+
+ public static final int S_IRWXG = 00070;
+ public static final int S_IRGRP = 00040;
+ public static final int S_IWGRP = 00020;
+ public static final int S_IXGRP = 00010;
+
+ public static final int S_IRWXO = 00007;
+ public static final int S_IROTH = 00004;
+ public static final int S_IWOTH = 00002;
+ public static final int S_IXOTH = 00001;
+
+
+ /**
+ * File status information. This class maps directly to the POSIX stat structure.
+ * @hide
+ */
+ public static final class FileStatus {
+ public int dev;
+ public int ino;
+ public int mode;
+ public int nlink;
+ public int uid;
+ public int gid;
+ public int rdev;
+ public long size;
+ public int blksize;
+ public long blocks;
+ public long atime;
+ public long mtime;
+ public long ctime;
+ }
+
+ /**
+ * Get the status for the given path. This is equivalent to the POSIX stat(2) system call.
+ * @param path The path of the file to be stat'd.
+ * @param status Optional argument to fill in. It will only fill in the status if the file
+ * exists.
+ * @return true if the file exists and false if it does not exist. If you do not have
+ * permission to stat the file, then this method will return false.
+ */
+ public static native boolean getFileStatus(String path, FileStatus status);
+
+ /** Regular expression for safe filenames: no spaces or metacharacters */
+ private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
+
+ public static native int setPermissions(String file, int mode, int uid, int gid);
+
+ public static native int getPermissions(String file, int[] outPermissions);
+
+ /** returns the FAT file system volume ID for the volume mounted
+ * at the given mount point, or -1 for failure
+ * @param mount point for FAT volume
+ * @return volume ID or -1
+ */
+ public static native int getFatVolumeId(String mountPoint);
+
+ // copy a file from srcFile to destFile, return true if succeed, return
+ // false if fail
+ public static boolean copyFile(File srcFile, File destFile) {
+ boolean result = false;
+ try {
+ InputStream in = new FileInputStream(srcFile);
+ try {
+ result = copyToFile(in, destFile);
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ result = false;
+ }
+ return result;
+ }
+
+ /**
+ * Copy data from a source stream to destFile.
+ * Return true if succeed, return false if failed.
+ */
+ public static boolean copyToFile(InputStream inputStream, File destFile) {
+ try {
+ OutputStream out = new FileOutputStream(destFile);
+ try {
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) >= 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ } finally {
+ out.close();
+ }
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Check if a filename is "safe" (no metacharacters or spaces).
+ * @param file The file to check
+ */
+ public static boolean isFilenameSafe(File file) {
+ // Note, we check whether it matches what's known to be safe,
+ // rather than what's known to be unsafe. Non-ASCII, control
+ // characters, etc. are all unsafe by default.
+ return SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
+ }
+
+ /**
+ * Read a text file into a String, optionally limiting the length.
+ * @param file to read (will not seek, so things like /proc files are OK)
+ * @param max length (positive for head, negative of tail, 0 for no limit)
+ * @param ellipsis to add of the file was truncated (can be null)
+ * @return the contents of the file, possibly truncated
+ * @throws IOException if something goes wrong reading the file
+ */
+ public static String readTextFile(File file, int max, String ellipsis) throws IOException {
+ InputStream input = new FileInputStream(file);
+ try {
+ if (max > 0) { // "head" mode: read the first N bytes
+ byte[] data = new byte[max + 1];
+ int length = input.read(data);
+ if (length <= 0) return "";
+ if (length <= max) return new String(data, 0, length);
+ if (ellipsis == null) return new String(data, 0, max);
+ return new String(data, 0, max) + ellipsis;
+ } else if (max < 0) { // "tail" mode: read it all, keep the last N
+ int len;
+ boolean rolled = false;
+ byte[] last = null, data = null;
+ do {
+ if (last != null) rolled = true;
+ byte[] tmp = last; last = data; data = tmp;
+ if (data == null) data = new byte[-max];
+ len = input.read(data);
+ } while (len == data.length);
+
+ if (last == null && len <= 0) return "";
+ if (last == null) return new String(data, 0, len);
+ if (len > 0) {
+ rolled = true;
+ System.arraycopy(last, len, last, 0, last.length - len);
+ System.arraycopy(data, 0, last, last.length - len, len);
+ }
+ if (ellipsis == null || !rolled) return new String(last);
+ return ellipsis + new String(last);
+ } else { // "cat" mode: read it all
+ ByteArrayOutputStream contents = new ByteArrayOutputStream();
+ int len;
+ byte[] data = new byte[1024];
+ do {
+ len = input.read(data);
+ if (len > 0) contents.write(data, 0, len);
+ } while (len == data.length);
+ return contents.toString();
+ }
+ } finally {
+ input.close();
+ }
+ }
+}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
new file mode 100644
index 0000000..2a32e54
--- /dev/null
+++ b/core/java/android/os/Handler.java
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.util.Log;
+import android.util.Printer;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * A Handler allows you to send and process {@link Message} and Runnable
+ * objects associated with a thread's {@link MessageQueue}. Each Handler
+ * instance is associated with a single thread and that thread's message
+ * queue. When you create a new Handler, it is bound to the thread /
+ * message queue of the thread that is creating it -- from that point on,
+ * it will deliver messages and runnables to that message queue and execute
+ * them as they come out of the message queue.
+ *
+ * <p>There are two main uses for a Handler: (1) to schedule messages and
+ * runnables to be executed as some point in the future; and (2) to enqueue
+ * an action to be performed on a different thread than your own.
+ *
+ * <p>Scheduling messages is accomplished with the
+ * {@link #post}, {@link #postAtTime(Runnable, long)},
+ * {@link #postDelayed}, {@link #sendEmptyMessage},
+ * {@link #sendMessage}, {@link #sendMessageAtTime}, and
+ * {@link #sendMessageDelayed} methods. The <em>post</em> versions allow
+ * you to enqueue Runnable objects to be called by the message queue when
+ * they are received; the <em>sendMessage</em> versions allow you to enqueue
+ * a {@link Message} object containing a bundle of data that will be
+ * processed by the Handler's {@link #handleMessage} method (requiring that
+ * you implement a subclass of Handler).
+ *
+ * <p>When posting or sending to a Handler, you can either
+ * allow the item to be processed as soon as the message queue is ready
+ * to do so, or specify a delay before it gets processed or absolute time for
+ * it to be processed. The latter two allow you to implement timeouts,
+ * ticks, and other timing-based behavior.
+ *
+ * <p>When a
+ * process is created for your application, its main thread is dedicated to
+ * running a message queue that takes care of managing the top-level
+ * application objects (activities, broadcast receivers, etc) and any windows
+ * they create. You can create your own threads, and communicate back with
+ * the main application thread through a Handler. This is done by calling
+ * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
+ * your new thread. The given Runnable or Message will than be scheduled
+ * in the Handler's message queue and processed when appropriate.
+ */
+public class Handler {
+ /*
+ * Set this flag to true to detect anonymous, local or member classes
+ * that extend this Handler class and that are not static. These kind
+ * of classes can potentially create leaks.
+ */
+ private static final boolean FIND_POTENTIAL_LEAKS = false;
+ private static final String TAG = "Handler";
+
+ /**
+ * Callback interface you can use when instantiating a Handler to avoid
+ * having to implement your own subclass of Handler.
+ */
+ public interface Callback {
+ public boolean handleMessage(Message msg);
+ }
+
+ /**
+ * Subclasses must implement this to receive messages.
+ */
+ public void handleMessage(Message msg) {
+ }
+
+ /**
+ * Handle system messages here.
+ */
+ public void dispatchMessage(Message msg) {
+ if (msg.callback != null) {
+ handleCallback(msg);
+ } else {
+ if (mCallback != null) {
+ if (mCallback.handleMessage(msg)) {
+ return;
+ }
+ }
+ handleMessage(msg);
+ }
+ }
+
+ /**
+ * Default constructor associates this handler with the queue for the
+ * current thread.
+ *
+ * If there isn't one, this handler won't be able to receive messages.
+ */
+ public Handler() {
+ if (FIND_POTENTIAL_LEAKS) {
+ final Class<? extends Handler> klass = getClass();
+ if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
+ (klass.getModifiers() & Modifier.STATIC) == 0) {
+ Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
+ klass.getCanonicalName());
+ }
+ }
+
+ mLooper = Looper.myLooper();
+ if (mLooper == null) {
+ throw new RuntimeException(
+ "Can't create handler inside thread that has not called Looper.prepare()");
+ }
+ mQueue = mLooper.mQueue;
+ mCallback = null;
+ }
+
+ /**
+ * Constructor associates this handler with the queue for the
+ * current thread and takes a callback interface in which you can handle
+ * messages.
+ */
+ public Handler(Callback callback) {
+ if (FIND_POTENTIAL_LEAKS) {
+ final Class<? extends Handler> klass = getClass();
+ if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
+ (klass.getModifiers() & Modifier.STATIC) == 0) {
+ Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
+ klass.getCanonicalName());
+ }
+ }
+
+ mLooper = Looper.myLooper();
+ if (mLooper == null) {
+ throw new RuntimeException(
+ "Can't create handler inside thread that has not called Looper.prepare()");
+ }
+ mQueue = mLooper.mQueue;
+ mCallback = callback;
+ }
+
+ /**
+ * Use the provided queue instead of the default one.
+ */
+ public Handler(Looper looper) {
+ mLooper = looper;
+ mQueue = looper.mQueue;
+ mCallback = null;
+ }
+
+ /**
+ * Use the provided queue instead of the default one and take a callback
+ * interface in which to handle messages.
+ */
+ public Handler(Looper looper, Callback callback) {
+ mLooper = looper;
+ mQueue = looper.mQueue;
+ mCallback = callback;
+ }
+
+ /**
+ * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
+ * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
+ * If you don't want that facility, just call Message.obtain() instead.
+ */
+ public final Message obtainMessage()
+ {
+ return Message.obtain(this);
+ }
+
+ /**
+ * Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message.
+ *
+ * @param what Value to assign to the returned Message.what field.
+ * @return A Message from the global message pool.
+ */
+ public final Message obtainMessage(int what)
+ {
+ return Message.obtain(this, what);
+ }
+
+ /**
+ *
+ * Same as {@link #obtainMessage()}, except that it also sets the what and obj members
+ * of the returned Message.
+ *
+ * @param what Value to assign to the returned Message.what field.
+ * @param obj Value to assign to the returned Message.obj field.
+ * @return A Message from the global message pool.
+ */
+ public final Message obtainMessage(int what, Object obj)
+ {
+ return Message.obtain(this, what, obj);
+ }
+
+ /**
+ *
+ * Same as {@link #obtainMessage()}, except that it also sets the what, arg1 and arg2 members of the returned
+ * Message.
+ * @param what Value to assign to the returned Message.what field.
+ * @param arg1 Value to assign to the returned Message.arg1 field.
+ * @param arg2 Value to assign to the returned Message.arg2 field.
+ * @return A Message from the global message pool.
+ */
+ public final Message obtainMessage(int what, int arg1, int arg2)
+ {
+ return Message.obtain(this, what, arg1, arg2);
+ }
+
+ /**
+ *
+ * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the
+ * returned Message.
+ * @param what Value to assign to the returned Message.what field.
+ * @param arg1 Value to assign to the returned Message.arg1 field.
+ * @param arg2 Value to assign to the returned Message.arg2 field.
+ * @param obj Value to assign to the returned Message.obj field.
+ * @return A Message from the global message pool.
+ */
+ public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
+ {
+ return Message.obtain(this, what, arg1, arg2, obj);
+ }
+
+ /**
+ * Causes the Runnable r to be added to the message queue.
+ * The runnable will be run on the thread to which this handler is
+ * attached.
+ *
+ * @param r The Runnable that will be executed.
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean post(Runnable r)
+ {
+ return sendMessageDelayed(getPostMessage(r), 0);
+ }
+
+ /**
+ * Causes the Runnable r to be added to the message queue, to be run
+ * at a specific time given by <var>uptimeMillis</var>.
+ * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
+ * The runnable will be run on the thread to which this handler is attached.
+ *
+ * @param r The Runnable that will be executed.
+ * @param uptimeMillis The absolute time at which the callback should run,
+ * using the {@link android.os.SystemClock#uptimeMillis} time-base.
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting. Note that a
+ * result of true does not mean the Runnable will be processed -- if
+ * the looper is quit before the delivery time of the message
+ * occurs then the message will be dropped.
+ */
+ public final boolean postAtTime(Runnable r, long uptimeMillis)
+ {
+ return sendMessageAtTime(getPostMessage(r), uptimeMillis);
+ }
+
+ /**
+ * Causes the Runnable r to be added to the message queue, to be run
+ * at a specific time given by <var>uptimeMillis</var>.
+ * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
+ * The runnable will be run on the thread to which this handler is attached.
+ *
+ * @param r The Runnable that will be executed.
+ * @param uptimeMillis The absolute time at which the callback should run,
+ * using the {@link android.os.SystemClock#uptimeMillis} time-base.
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting. Note that a
+ * result of true does not mean the Runnable will be processed -- if
+ * the looper is quit before the delivery time of the message
+ * occurs then the message will be dropped.
+ *
+ * @see android.os.SystemClock#uptimeMillis
+ */
+ public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
+ {
+ return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
+ }
+
+ /**
+ * Causes the Runnable r to be added to the message queue, to be run
+ * after the specified amount of time elapses.
+ * The runnable will be run on the thread to which this handler
+ * is attached.
+ *
+ * @param r The Runnable that will be executed.
+ * @param delayMillis The delay (in milliseconds) until the Runnable
+ * will be executed.
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting. Note that a
+ * result of true does not mean the Runnable will be processed --
+ * if the looper is quit before the delivery time of the message
+ * occurs then the message will be dropped.
+ */
+ public final boolean postDelayed(Runnable r, long delayMillis)
+ {
+ return sendMessageDelayed(getPostMessage(r), delayMillis);
+ }
+
+ /**
+ * Posts a message to an object that implements Runnable.
+ * Causes the Runnable r to executed on the next iteration through the
+ * message queue. The runnable will be run on the thread to which this
+ * handler is attached.
+ * <b>This method is only for use in very special circumstances -- it
+ * can easily starve the message queue, cause ordering problems, or have
+ * other unexpected side-effects.</b>
+ *
+ * @param r The Runnable that will be executed.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean postAtFrontOfQueue(Runnable r)
+ {
+ return sendMessageAtFrontOfQueue(getPostMessage(r));
+ }
+
+ /**
+ * Remove any pending posts of Runnable r that are in the message queue.
+ */
+ public final void removeCallbacks(Runnable r)
+ {
+ mQueue.removeMessages(this, r, null);
+ }
+
+ /**
+ * Remove any pending posts of Runnable <var>r</var> with Object
+ * <var>token</var> that are in the message queue.
+ */
+ public final void removeCallbacks(Runnable r, Object token)
+ {
+ mQueue.removeMessages(this, r, token);
+ }
+
+ /**
+ * Pushes a message onto the end of the message queue after all pending messages
+ * before the current time. It will be received in {@link #handleMessage},
+ * in the thread attached to this handler.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean sendMessage(Message msg)
+ {
+ return sendMessageDelayed(msg, 0);
+ }
+
+ /**
+ * Sends a Message containing only the what value.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean sendEmptyMessage(int what)
+ {
+ return sendEmptyMessageDelayed(what, 0);
+ }
+
+ /**
+ * Sends a Message containing only the what value, to be delivered
+ * after the specified amount of time elapses.
+ * @see #sendMessageDelayed(android.os.Message, long)
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ return sendMessageDelayed(msg, delayMillis);
+ }
+
+ /**
+ * Sends a Message containing only the what value, to be delivered
+ * at a specific time.
+ * @see #sendMessageAtTime(android.os.Message, long)
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+
+ public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ return sendMessageAtTime(msg, uptimeMillis);
+ }
+
+ /**
+ * Enqueue a message into the message queue after all pending messages
+ * before (current time + delayMillis). You will receive it in
+ * {@link #handleMessage}, in the thread attached to this handler.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting. Note that a
+ * result of true does not mean the message will be processed -- if
+ * the looper is quit before the delivery time of the message
+ * occurs then the message will be dropped.
+ */
+ public final boolean sendMessageDelayed(Message msg, long delayMillis)
+ {
+ if (delayMillis < 0) {
+ delayMillis = 0;
+ }
+ return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
+ }
+
+ /**
+ * Enqueue a message into the message queue after all pending messages
+ * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
+ * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
+ * You will receive it in {@link #handleMessage}, in the thread attached
+ * to this handler.
+ *
+ * @param uptimeMillis The absolute time at which the message should be
+ * delivered, using the
+ * {@link android.os.SystemClock#uptimeMillis} time-base.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting. Note that a
+ * result of true does not mean the message will be processed -- if
+ * the looper is quit before the delivery time of the message
+ * occurs then the message will be dropped.
+ */
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis)
+ {
+ boolean sent = false;
+ MessageQueue queue = mQueue;
+ if (queue != null) {
+ msg.target = this;
+ sent = queue.enqueueMessage(msg, uptimeMillis);
+ }
+ else {
+ RuntimeException e = new RuntimeException(
+ this + " sendMessageAtTime() called with no mQueue");
+ Log.w("Looper", e.getMessage(), e);
+ }
+ return sent;
+ }
+
+ /**
+ * Enqueue a message at the front of the message queue, to be processed on
+ * the next iteration of the message loop. You will receive it in
+ * {@link #handleMessage}, in the thread attached to this handler.
+ * <b>This method is only for use in very special circumstances -- it
+ * can easily starve the message queue, cause ordering problems, or have
+ * other unexpected side-effects.</b>
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean sendMessageAtFrontOfQueue(Message msg)
+ {
+ boolean sent = false;
+ MessageQueue queue = mQueue;
+ if (queue != null) {
+ msg.target = this;
+ sent = queue.enqueueMessage(msg, 0);
+ }
+ else {
+ RuntimeException e = new RuntimeException(
+ this + " sendMessageAtTime() called with no mQueue");
+ Log.w("Looper", e.getMessage(), e);
+ }
+ return sent;
+ }
+
+ /**
+ * Remove any pending posts of messages with code 'what' that are in the
+ * message queue.
+ */
+ public final void removeMessages(int what) {
+ mQueue.removeMessages(this, what, null, true);
+ }
+
+ /**
+ * Remove any pending posts of messages with code 'what' and whose obj is
+ * 'object' that are in the message queue.
+ */
+ public final void removeMessages(int what, Object object) {
+ mQueue.removeMessages(this, what, object, true);
+ }
+
+ /**
+ * Remove any pending posts of callbacks and sent messages whose
+ * <var>obj</var> is <var>token</var>.
+ */
+ public final void removeCallbacksAndMessages(Object token) {
+ mQueue.removeCallbacksAndMessages(this, token);
+ }
+
+ /**
+ * Check if there are any pending posts of messages with code 'what' in
+ * the message queue.
+ */
+ public final boolean hasMessages(int what) {
+ return mQueue.removeMessages(this, what, null, false);
+ }
+
+ /**
+ * Check if there are any pending posts of messages with code 'what' and
+ * whose obj is 'object' in the message queue.
+ */
+ public final boolean hasMessages(int what, Object object) {
+ return mQueue.removeMessages(this, what, object, false);
+ }
+
+ // if we can get rid of this method, the handler need not remember its loop
+ // we could instead export a getMessageQueue() method...
+ public final Looper getLooper() {
+ return mLooper;
+ }
+
+ public final void dump(Printer pw, String prefix) {
+ pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
+ if (mLooper == null) {
+ pw.println(prefix + "looper uninitialized");
+ } else {
+ mLooper.dump(pw, prefix + " ");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Handler{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + "}";
+ }
+
+ final IMessenger getIMessenger() {
+ synchronized (mQueue) {
+ if (mMessenger != null) {
+ return mMessenger;
+ }
+ mMessenger = new MessengerImpl();
+ return mMessenger;
+ }
+ }
+
+ private final class MessengerImpl extends IMessenger.Stub {
+ public void send(Message msg) {
+ Handler.this.sendMessage(msg);
+ }
+ }
+
+ private final Message getPostMessage(Runnable r) {
+ Message m = Message.obtain();
+ m.callback = r;
+ return m;
+ }
+
+ private final Message getPostMessage(Runnable r, Object token) {
+ Message m = Message.obtain();
+ m.obj = token;
+ m.callback = r;
+ return m;
+ }
+
+ private final void handleCallback(Message message) {
+ message.callback.run();
+ }
+
+ final MessageQueue mQueue;
+ final Looper mLooper;
+ final Callback mCallback;
+ IMessenger mMessenger;
+}
diff --git a/core/java/android/os/HandlerState.java b/core/java/android/os/HandlerState.java
new file mode 100644
index 0000000..0708f7d
--- /dev/null
+++ b/core/java/android/os/HandlerState.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * {@hide}
+ */
+public abstract class HandlerState {
+ public HandlerState() {
+ }
+
+ public void enter(Message message) {
+ }
+
+ public abstract void processMessage(Message message);
+
+ public void exit(Message message) {
+ }
+}
diff --git a/core/java/android/os/HandlerStateMachine.java b/core/java/android/os/HandlerStateMachine.java
new file mode 100644
index 0000000..d004a25
--- /dev/null
+++ b/core/java/android/os/HandlerStateMachine.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.util.Log;
+import android.util.LogPrinter;
+
+/**
+ * {@hide}
+ *
+ * Implement a state machine where each state is an object,
+ * HandlerState. Each HandlerState must implement processMessage
+ * and optionally enter/exit. When a state machine is created
+ * the initial state must be set. When messages are sent to
+ * a state machine the current state's processMessage method is
+ * invoked. If this is the first message for this state the
+ * enter method is called prior to processMessage and when
+ * transtionTo is invoked the state's exit method will be
+ * called after returning from processMessage.
+ *
+ * If a message should be handled in a different state the
+ * processMessage method may call deferMessage. This causes
+ * the message to be saved on a list until transitioning
+ * to a new state, at which time all of the deferred messages
+ * will be put on the front of the state machines queue and
+ * processed by the new current state's processMessage
+ * method.
+ *
+ * Below is an example state machine with two state's, S1 and S2.
+ * The initial state is S1 which defers all messages and only
+ * transition to S2 when message.what == TEST_WHAT_2. State S2
+ * will process each messages until it receives TEST_WHAT_2
+ * where it will transition back to S1:
+<code>
+ class StateMachine1 extends HandlerStateMachine {
+ private static final int TEST_WHAT_1 = 1;
+ private static final int TEST_WHAT_2 = 2;
+
+ StateMachine1(String name) {
+ super(name);
+ setInitialState(mS1);
+ }
+
+ class S1 extends HandlerState {
+ @Override public void enter(Message message) {
+ }
+
+ @Override public void processMessage(Message message) {
+ deferMessage(message);
+ if (message.what == TEST_WHAT_2) {
+ transitionTo(mS2);
+ }
+ }
+
+ @Override public void exit(Message message) {
+ }
+ }
+
+ class S2 extends HandlerState {
+ @Override public void processMessage(Message message) {
+ // Do some processing
+ if (message.what == TEST_WHAT_2) {
+ transtionTo(mS1);
+ }
+ }
+ }
+
+ private S1 mS1 = new S1();
+ private S2 mS2 = new S2();
+ }
+</code>
+ */
+public class HandlerStateMachine {
+
+ private boolean mDbg = false;
+ private static final String TAG = "HandlerStateMachine";
+ private String mName;
+ private SmHandler mHandler;
+ private HandlerThread mHandlerThread;
+
+ /**
+ * Handle messages sent to the state machine by calling
+ * the current state's processMessage. It also handles
+ * the enter/exit calls and placing any deferred messages
+ * back onto the queue when transitioning to a new state.
+ */
+ class SmHandler extends Handler {
+
+ SmHandler(Looper looper) {
+ super(looper);
+ }
+
+ /**
+ * This will dispatch the message to the
+ * current state's processMessage.
+ */
+ @Override
+ final public void handleMessage(Message msg) {
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage E");
+ if (mDestState != null) {
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage; new destation call enter");
+ mCurrentState = mDestState;
+ mDestState = null;
+ mCurrentState.enter(msg);
+ }
+ if (mCurrentState != null) {
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage; call processMessage");
+ mCurrentState.processMessage(msg);
+ } else {
+ /* Strange no state to execute */
+ Log.e(TAG, "handleMessage: no current state, did you call setInitialState");
+ }
+
+ if (mDestState != null) {
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage; new destination call exit");
+ mCurrentState.exit(msg);
+
+ /**
+ * Place the messages from the deferred queue:t
+ * on to the Handler's message queue in the
+ * same order that they originally arrived.
+ *
+ * We set cur.when = 0 to circumvent the check
+ * that this message has already been sent.
+ */
+ while (mDeferredMessages != null) {
+ Message cur = mDeferredMessages;
+ mDeferredMessages = mDeferredMessages.next;
+ cur.when = 0;
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage; queue deferred message what="
+ + cur.what + " target=" + cur.target);
+ sendMessageAtFrontOfQueue(cur);
+ }
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage X");
+ }
+ }
+
+ public HandlerState mCurrentState;
+ public HandlerState mDestState;
+ public Message mDeferredMessages;
+ }
+
+ /**
+ * Create an active StateMachine, one that has a
+ * dedicated thread/looper/queue.
+ */
+ public HandlerStateMachine(String name) {
+ mName = name;
+ mHandlerThread = new HandlerThread(name);
+ mHandlerThread.start();
+ mHandler = new SmHandler(mHandlerThread.getLooper());
+ }
+
+ /**
+ * Get a message and set Message.target = this.
+ */
+ public final Message obtainMessage()
+ {
+ Message msg = Message.obtain(mHandler);
+ if (mDbg) Log.d(TAG, "StateMachine.obtainMessage() EX target=" + msg.target);
+ return msg;
+ }
+
+ /**
+ * Get a message and set Message.target = this and
+ * Message.what = what.
+ */
+ public final Message obtainMessage(int what) {
+ Message msg = Message.obtain(mHandler, what);
+ if (mDbg) {
+ Log.d(TAG, "StateMachine.obtainMessage(what) EX what=" + msg.what +
+ " target=" + msg.target);
+ }
+ return msg;
+ }
+
+ /**
+ * Enqueue a message to this state machine.
+ */
+ public final void sendMessage(Message msg) {
+ if (mDbg) Log.d(TAG, "StateMachine.sendMessage EX msg.what=" + msg.what);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Enqueue a message to this state machine after a delay.
+ */
+ public final void sendMessageDelayed(Message msg, long delayMillis) {
+ if (mDbg) {
+ Log.d(TAG, "StateMachine.sendMessageDelayed EX msg.what="
+ + msg.what + " delay=" + delayMillis);
+ }
+ mHandler.sendMessageDelayed(msg, delayMillis);
+ }
+
+ /**
+ * Set the initial state. This must be invoked before
+ * and messages are sent to the state machine.
+ */
+ public void setInitialState(HandlerState initialState) {
+ if (mDbg) {
+ Log.d(TAG, "StateMachine.setInitialState EX initialState"
+ + initialState.getClass().getName());
+ }
+ mHandler.mDestState = initialState;
+ }
+
+ /**
+ * transition to destination state. Upon returning
+ * from processMessage the current state's exit will
+ * be executed and upon the next message arriving
+ * destState.enter will be invoked.
+ */
+ final public void transitionTo(HandlerState destState) {
+ if (mDbg) {
+ Log.d(TAG, "StateMachine.transitionTo EX destState"
+ + destState.getClass().getName());
+ }
+ mHandler.mDestState = destState;
+ }
+
+ /**
+ * Defer this message until next state transition.
+ * Upon transitioning all deferred messages will be
+ * placed on the queue and reprocessed in the original
+ * order. (i.e. The next state the oldest messages will
+ * be processed first)
+ */
+ final public void deferMessage(Message msg) {
+ if (mDbg) {
+ Log.d(TAG, "StateMachine.deferMessage EX mDeferredMessages="
+ + mHandler.mDeferredMessages);
+ }
+
+ /* Copy the "msg" to "newMsg" as "msg" will be recycled */
+ Message newMsg = obtainMessage();
+ newMsg.copyFrom(msg);
+
+ /* Place on front of queue */
+ newMsg.next = mHandler.mDeferredMessages;
+ mHandler.mDeferredMessages = newMsg;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * @return Handler
+ */
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ /**
+ * @return if debugging is enabled
+ */
+ public boolean isDbg() {
+ return mDbg;
+ }
+
+ /**
+ * Set debug enable/disabled.
+ */
+ public void setDbg(boolean dbg) {
+ mDbg = dbg;
+ if (mDbg) {
+ mHandlerThread.getLooper().setMessageLogging(new LogPrinter(Log.VERBOSE, TAG));
+ } else {
+ mHandlerThread.getLooper().setMessageLogging(null);
+ }
+ }
+}
diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java
new file mode 100644
index 0000000..0ce86db
--- /dev/null
+++ b/core/java/android/os/HandlerThread.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * Handy class for starting a new thread that has a looper. The looper can then be
+ * used to create handler classes. Note that start() must still be called.
+ */
+public class HandlerThread extends Thread {
+ private int mPriority;
+ private int mTid = -1;
+ private Looper mLooper;
+
+ public HandlerThread(String name) {
+ super(name);
+ mPriority = Process.THREAD_PRIORITY_DEFAULT;
+ }
+
+ /**
+ * Constructs a HandlerThread.
+ * @param name
+ * @param priority The priority to run the thread at. The value supplied must be from
+ * {@link android.os.Process} and not from java.lang.Thread.
+ */
+ public HandlerThread(String name, int priority) {
+ super(name);
+ mPriority = priority;
+ }
+
+ /**
+ * Call back method that can be explicitly over ridden if needed to execute some
+ * setup before Looper loops.
+ */
+ protected void onLooperPrepared() {
+ }
+
+ public void run() {
+ mTid = Process.myTid();
+ Looper.prepare();
+ synchronized (this) {
+ mLooper = Looper.myLooper();
+ Process.setThreadPriority(mPriority);
+ notifyAll();
+ }
+ onLooperPrepared();
+ Looper.loop();
+ mTid = -1;
+ }
+
+ /**
+ * This method returns the Looper associated with this thread. If this thread not been started
+ * or for any reason is isAlive() returns false, this method will return null. If this thread
+ * has been started, this method will blocked until the looper has been initialized.
+ * @return The looper.
+ */
+ public Looper getLooper() {
+ if (!isAlive()) {
+ return null;
+ }
+
+ // If the thread has been started, wait until the looper has been created.
+ synchronized (this) {
+ while (isAlive() && mLooper == null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ return mLooper;
+ }
+
+ /**
+ * Returns the identifier of this thread. See Process.myTid().
+ */
+ public int getThreadId() {
+ return mTid;
+ }
+}
diff --git a/core/java/android/os/Hardware.java b/core/java/android/os/Hardware.java
new file mode 100644
index 0000000..3b6c9d7
--- /dev/null
+++ b/core/java/android/os/Hardware.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * {@hide}
+ */
+public class Hardware
+{
+ /**
+ * Control the LED.
+ */
+ public static native int setLedState(int colorARGB, int onMS, int offMS);
+
+ /**
+ * Control the Flashlight
+ */
+ public static native boolean getFlashlightEnabled();
+ public static native void setFlashlightEnabled(boolean on);
+ public static native void enableCameraFlash(int milliseconds);
+
+ /**
+ * Control the backlights
+ */
+ public static native void setScreenBacklight(int brightness);
+ public static native void setKeyboardBacklight(boolean on);
+ public static native void setButtonBacklight(boolean on);
+}
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
new file mode 100644
index 0000000..5c40c9a0
--- /dev/null
+++ b/core/java/android/os/IBinder.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Base interface for a remotable object, the core part of a lightweight
+ * remote procedure call mechanism designed for high performance when
+ * performing in-process and cross-process calls. This
+ * interface describes the abstract protocol for interacting with a
+ * remotable object. Do not implement this interface directly, instead
+ * extend from {@link Binder}.
+ *
+ * <p>The key IBinder API is {@link #transact transact()} matched by
+ * {@link Binder#onTransact Binder.onTransact()}. These
+ * methods allow you to send a call to an IBinder object and receive a
+ * call coming in to a Binder object, respectively. This transaction API
+ * is synchronous, such that a call to {@link #transact transact()} does not
+ * return until the target has returned from
+ * {@link Binder#onTransact Binder.onTransact()}; this is the
+ * expected behavior when calling an object that exists in the local
+ * process, and the underlying inter-process communication (IPC) mechanism
+ * ensures that these same semantics apply when going across processes.
+ *
+ * <p>The data sent through transact() is a {@link Parcel}, a generic buffer
+ * of data that also maintains some meta-data about its contents. The meta
+ * data is used to manage IBinder object references in the buffer, so that those
+ * references can be maintained as the buffer moves across processes. This
+ * mechanism ensures that when an IBinder is written into a Parcel and sent to
+ * another process, if that other process sends a reference to that same IBinder
+ * back to the original process, then the original process will receive the
+ * same IBinder object back. These semantics allow IBinder/Binder objects to
+ * be used as a unique identity (to serve as a token or for other purposes)
+ * that can be managed across processes.
+ *
+ * <p>The system maintains a pool of transaction threads in each process that
+ * it runs in. These threads are used to dispatch all
+ * IPCs coming in from other processes. For example, when an IPC is made from
+ * process A to process B, the calling thread in A blocks in transact() as
+ * it sends the transaction to process B. The next available pool thread in
+ * B receives the incoming transaction, calls Binder.onTransact() on the target
+ * object, and replies with the result Parcel. Upon receiving its result, the
+ * thread in process A returns to allow its execution to continue. In effect,
+ * other processes appear to use as additional threads that you did not create
+ * executing in your own process.
+ *
+ * <p>The Binder system also supports recursion across processes. For example
+ * if process A performs a transaction to process B, and process B while
+ * handling that transaction calls transact() on an IBinder that is implemented
+ * in A, then the thread in A that is currently waiting for the original
+ * transaction to finish will take care of calling Binder.onTransact() on the
+ * object being called by B. This ensures that the recursion semantics when
+ * calling remote binder object are the same as when calling local objects.
+ *
+ * <p>When working with remote objects, you often want to find out when they
+ * are no longer valid. There are three ways this can be determined:
+ * <ul>
+ * <li> The {@link #transact transact()} method will throw a
+ * {@link RemoteException} exception if you try to call it on an IBinder
+ * whose process no longer exists.
+ * <li> The {@link #pingBinder()} method can be called, and will return false
+ * if the remote process no longer exists.
+ * <li> The {@link #linkToDeath linkToDeath()} method can be used to register
+ * a {@link DeathRecipient} with the IBinder, which will be called when its
+ * containing process goes away.
+ * </ul>
+ *
+ * @see Binder
+ */
+public interface IBinder {
+ /**
+ * The first transaction code available for user commands.
+ */
+ int FIRST_CALL_TRANSACTION = 0x00000001;
+ /**
+ * The last transaction code available for user commands.
+ */
+ int LAST_CALL_TRANSACTION = 0x00ffffff;
+
+ /**
+ * IBinder protocol transaction code: pingBinder().
+ */
+ int PING_TRANSACTION = ('_'<<24)|('P'<<16)|('N'<<8)|'G';
+
+ /**
+ * IBinder protocol transaction code: dump internal state.
+ */
+ int DUMP_TRANSACTION = ('_'<<24)|('D'<<16)|('M'<<8)|'P';
+
+ /**
+ * IBinder protocol transaction code: interrogate the recipient side
+ * of the transaction for its canonical interface descriptor.
+ */
+ int INTERFACE_TRANSACTION = ('_'<<24)|('N'<<16)|('T'<<8)|'F';
+
+ /**
+ * Flag to {@link #transact}: this is a one-way call, meaning that the
+ * caller returns immediately, without waiting for a result from the
+ * callee.
+ */
+ int FLAG_ONEWAY = 0x00000001;
+
+ /**
+ * Get the canonical name of the interface supported by this binder.
+ */
+ public String getInterfaceDescriptor() throws RemoteException;
+
+ /**
+ * Check to see if the object still exists.
+ *
+ * @return Returns false if the
+ * hosting process is gone, otherwise the result (always by default
+ * true) returned by the pingBinder() implementation on the other
+ * side.
+ */
+ public boolean pingBinder();
+
+ /**
+ * Check to see if the process that the binder is in is still alive.
+ *
+ * @return false if the process is not alive. Note that if it returns
+ * true, the process may have died while the call is returning.
+ */
+ public boolean isBinderAlive();
+
+ /**
+ * Attempt to retrieve a local implementation of an interface
+ * for this Binder object. If null is returned, you will need
+ * to instantiate a proxy class to marshall calls through
+ * the transact() method.
+ */
+ public IInterface queryLocalInterface(String descriptor);
+
+ /**
+ * Print the object's state into the given stream.
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param args additional arguments to the dump request.
+ */
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException;
+
+ /**
+ * Perform a generic operation with the object.
+ *
+ * @param code The action to perform. This should
+ * be a number between {@link #FIRST_CALL_TRANSACTION} and
+ * {@link #LAST_CALL_TRANSACTION}.
+ * @param data Marshalled data to send to the target. Most not be null.
+ * If you are not sending any data, you must create an empty Parcel
+ * that is given here.
+ * @param reply Marshalled data to be received from the target. May be
+ * null if you are not interested in the return value.
+ * @param flags Additional operation flags. Either 0 for a normal
+ * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
+ */
+ public boolean transact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException;
+
+ /**
+ * Interface for receiving a callback when the process hosting an IBinder
+ * has gone away.
+ *
+ * @see #linkToDeath
+ */
+ public interface DeathRecipient {
+ public void binderDied();
+ }
+
+ /**
+ * Register the recipient for a notification if this binder
+ * goes away. If this binder object unexpectedly goes away
+ * (typically because its hosting process has been killed),
+ * then the given {@link DeathRecipient}'s
+ * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
+ * will be called.
+ *
+ * <p>You will only receive death notifications for remote binders,
+ * as local binders by definition can't die without you dying as well.
+ *
+ * @throws Throws {@link RemoteException} if the target IBinder's
+ * process has already died.
+ *
+ * @see #unlinkToDeath
+ */
+ public void linkToDeath(DeathRecipient recipient, int flags)
+ throws RemoteException;
+
+ /**
+ * Remove a previously registered death notification.
+ * The recipient will no longer be called if this object
+ * dies.
+ *
+ * @return Returns true if the <var>recipient</var> is successfully
+ * unlinked, assuring you that its
+ * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
+ * will not be called. Returns false if the target IBinder has already
+ * died, meaning the method has been (or soon will be) called.
+ *
+ * @throws Throws {@link java.util.NoSuchElementException} if the given
+ * <var>recipient</var> has not been registered with the IBinder, and
+ * the IBinder is still alive. Note that if the <var>recipient</var>
+ * was never registered, but the IBinder has already died, then this
+ * exception will <em>not</em> be thrown, and you will receive a false
+ * return value instead.
+ */
+ public boolean unlinkToDeath(DeathRecipient recipient, int flags);
+}
diff --git a/core/java/android/os/ICheckinService.aidl b/core/java/android/os/ICheckinService.aidl
new file mode 100644
index 0000000..e56b55d
--- /dev/null
+++ b/core/java/android/os/ICheckinService.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.IParentalControlCallback;
+
+/**
+ * System private API for direct access to the checkin service.
+ * Users should use the content provider instead.
+ *
+ * @see android.provider.Checkin
+ * {@hide}
+ */
+interface ICheckinService {
+ /** Synchronously attempt a checkin with the server, return true
+ * on success.
+ * @throws IllegalStateException whenever an error occurs. The
+ * cause of the exception will be the real exception:
+ * IOException for network errors, JSONException for invalid
+ * server responses, etc.
+ */
+ boolean checkin();
+
+ /** Direct submission of crash data; returns after writing the crash. */
+ void reportCrashSync(in byte[] crashData);
+
+ /** Asynchronous "fire and forget" version of crash reporting. */
+ oneway void reportCrashAsync(in byte[] crashData);
+
+ /** Reboot into the recovery system and wipe all user data. */
+ void masterClear();
+
+ /**
+ * Determine if the device is under parental control. Return null if
+ * we are unable to check the parental control status.
+ */
+ void getParentalControlState(IParentalControlCallback p,
+ String requestingApp);
+}
diff --git a/core/java/android/os/IHardwareService.aidl b/core/java/android/os/IHardwareService.aidl
new file mode 100755
index 0000000..4f6029f
--- /dev/null
+++ b/core/java/android/os/IHardwareService.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+interface IHardwareService
+{
+ // Vibrator support
+ void vibrate(long milliseconds);
+ void vibratePattern(in long[] pattern, int repeat, IBinder token);
+ void cancelVibrate();
+
+ // flashlight support
+ boolean getFlashlightEnabled();
+ void setFlashlightEnabled(boolean on);
+ void enableCameraFlash(int milliseconds);
+
+ // backlight support
+ void setScreenBacklight(int brightness);
+ void setKeyboardBacklight(boolean on);
+ void setButtonBacklight(boolean on);
+
+ // LED support
+ void setLedState(int colorARGB, int onMS, int offMS);
+}
+
diff --git a/core/java/android/os/IInterface.java b/core/java/android/os/IInterface.java
new file mode 100644
index 0000000..2a2605a
--- /dev/null
+++ b/core/java/android/os/IInterface.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * Base class for Binder interfaces. When defining a new interface,
+ * you must derive it from IInterface.
+ */
+public interface IInterface
+{
+ /**
+ * Retrieve the Binder object associated with this interface.
+ * You must use this instead of a plain cast, so that proxy objects
+ * can return the correct result.
+ */
+ public IBinder asBinder();
+}
diff --git a/core/java/android/os/IMessenger.aidl b/core/java/android/os/IMessenger.aidl
new file mode 100644
index 0000000..e4a8431
--- /dev/null
+++ b/core/java/android/os/IMessenger.aidl
@@ -0,0 +1,25 @@
+/* //device/java/android/android/app/IActivityPendingResult.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+import android.os.Message;
+
+/** @hide */
+oneway interface IMessenger {
+ void send(in Message msg);
+}
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
new file mode 100644
index 0000000..88dae85
--- /dev/null
+++ b/core/java/android/os/IMountService.aidl
@@ -0,0 +1,66 @@
+/* //device/java/android/android/os/IUsb.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+/** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
+ * In particular, the ordering of the methods below must match the
+ * _TRANSACTION enum in IMountService.cpp
+ * @hide
+ */
+interface IMountService
+{
+ /**
+ * Is mass storage support enabled?
+ */
+ boolean getMassStorageEnabled();
+
+ /**
+ * Enable or disable mass storage support.
+ */
+ void setMassStorageEnabled(boolean enabled);
+
+ /**
+ * Is mass storage connected?
+ */
+ boolean getMassStorageConnected();
+
+ /**
+ * Mount external storage at given mount point.
+ */
+ void mountMedia(String mountPoint);
+
+ /**
+ * Safely unmount external storage at given mount point.
+ */
+ void unmountMedia(String mountPoint);
+
+ /**
+ * Format external storage given a mount point
+ */
+ void formatMedia(String mountPoint);
+
+ /**
+ * Returns true if media notification sounds are enabled.
+ */
+ boolean getPlayNotificationSounds();
+
+ /**
+ * Sets whether or not media notification sounds are played.
+ */
+ void setPlayNotificationSounds(boolean value);
+}
diff --git a/core/java/android/os/INetStatService.aidl b/core/java/android/os/INetStatService.aidl
new file mode 100644
index 0000000..a8f3de0
--- /dev/null
+++ b/core/java/android/os/INetStatService.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 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 android.os;
+
+/**
+ * Retrieves packet and byte counts for the phone data interface,
+ * and for all interfaces.
+ * Used for the data activity icon and the phone status in Settings.
+ *
+ * {@hide}
+ */
+interface INetStatService {
+ long getMobileTxPackets();
+ long getMobileRxPackets();
+ long getMobileTxBytes();
+ long getMobileRxBytes();
+ long getTotalTxPackets();
+ long getTotalRxPackets();
+ long getTotalTxBytes();
+ long getTotalRxBytes();
+}
diff --git a/core/java/android/os/IParentalControlCallback.aidl b/core/java/android/os/IParentalControlCallback.aidl
new file mode 100644
index 0000000..2f1a563
--- /dev/null
+++ b/core/java/android/os/IParentalControlCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2008 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 android.os;
+
+import com.google.android.net.ParentalControlState;
+
+/**
+ * This callback interface is used to deliver the parental control state to the calling application.
+ * {@hide}
+ */
+oneway interface IParentalControlCallback {
+ void onResult(in ParentalControlState state);
+}
diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl
new file mode 100644
index 0000000..73a68f1
--- /dev/null
+++ b/core/java/android/os/IPermissionController.aidl
@@ -0,0 +1,23 @@
+/* //device/java/android/android/os/IPowerManager.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+/** @hide */
+interface IPermissionController {
+ boolean checkPermission(String permission, int pid, int uid);
+}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
new file mode 100644
index 0000000..5486920
--- /dev/null
+++ b/core/java/android/os/IPowerManager.aidl
@@ -0,0 +1,33 @@
+/* //device/java/android/android/os/IPowerManager.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+/** @hide */
+interface IPowerManager
+{
+ void acquireWakeLock(int flags, IBinder lock, String tag);
+ void goToSleep(long time);
+ void releaseWakeLock(IBinder lock);
+ void userActivity(long when, boolean noChangeLights);
+ void userActivityWithForce(long when, boolean noChangeLights, boolean force);
+ void setPokeLock(int pokey, IBinder lock, String tag);
+ void setStayOnSetting(int val);
+ long getScreenOnTime();
+ void preventScreenOn(boolean prevent);
+ void setScreenBrightnessOverride(int brightness);
+}
diff --git a/core/java/android/os/IServiceManager.java b/core/java/android/os/IServiceManager.java
new file mode 100644
index 0000000..9a5ff47
--- /dev/null
+++ b/core/java/android/os/IServiceManager.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * Basic interface for finding and publishing system services.
+ *
+ * An implementation of this interface is usually published as the
+ * global context object, which can be retrieved via
+ * BinderNative.getContextObject(). An easy way to retrieve this
+ * is with the static method BnServiceManager.getDefault().
+ *
+ * @hide
+ */
+public interface IServiceManager extends IInterface
+{
+ /**
+ * Retrieve an existing service called @a name from the
+ * service manager. Blocks for a few seconds waiting for it to be
+ * published if it does not already exist.
+ */
+ public IBinder getService(String name) throws RemoteException;
+
+ /**
+ * Retrieve an existing service called @a name from the
+ * service manager. Non-blocking.
+ */
+ public IBinder checkService(String name) throws RemoteException;
+
+ /**
+ * Place a new @a service called @a name into the service
+ * manager.
+ */
+ public void addService(String name, IBinder service) throws RemoteException;
+
+ /**
+ * Return a list of all currently running services.
+ */
+ public String[] listServices() throws RemoteException;
+
+ /**
+ * Assign a permission controller to the service manager. After set, this
+ * interface is checked before any services are added.
+ */
+ public void setPermissionController(IPermissionController controller)
+ throws RemoteException;
+
+ static final String descriptor = "android.os.IServiceManager";
+
+ int GET_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
+ int CHECK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
+ int ADD_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
+ int LIST_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
+ int CHECK_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
+ int SET_PERMISSION_CONTROLLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+5;
+}
diff --git a/core/java/android/os/LocalPowerManager.java b/core/java/android/os/LocalPowerManager.java
new file mode 100644
index 0000000..55d7972
--- /dev/null
+++ b/core/java/android/os/LocalPowerManager.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** @hide */
+public interface LocalPowerManager {
+ public static final int OTHER_EVENT = 0;
+ public static final int CHEEK_EVENT = 1;
+ public static final int TOUCH_EVENT = 2;
+ public static final int BUTTON_EVENT = 3; // Button and trackball events.
+
+ public static final int POKE_LOCK_IGNORE_CHEEK_EVENTS = 0x1;
+ public static final int POKE_LOCK_SHORT_TIMEOUT = 0x2;
+ public static final int POKE_LOCK_MEDIUM_TIMEOUT = 0x4;
+
+ public static final int POKE_LOCK_TIMEOUT_MASK = 0x6;
+
+ void goToSleep(long time);
+
+ // notify power manager when keyboard is opened/closed
+ void setKeyboardVisibility(boolean visible);
+
+ // when the keyguard is up, it manages the power state, and userActivity doesn't do anything.
+ void enableUserActivity(boolean enabled);
+
+ // the same as the method on PowerManager
+ public void userActivity(long time, boolean noChangeLights, int eventType);
+}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
new file mode 100644
index 0000000..9581893
--- /dev/null
+++ b/core/java/android/os/Looper.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.util.Config;
+import android.util.Printer;
+
+/**
+ * Class used to run a message loop for a thread. Threads by default do
+ * not have a message loop associated with them; to create one, call
+ * {@link #prepare} in the thread that is to run the loop, and then
+ * {@link #loop} to have it process messages until the loop is stopped.
+ *
+ * <p>Most interaction with a message loop is through the
+ * {@link Handler} class.
+ *
+ * <p>This is a typical example of the implementation of a Looper thread,
+ * using the separation of {@link #prepare} and {@link #loop} to create an
+ * initial Handler to communicate with the Looper.
+ *
+ * <pre>
+ * class LooperThread extends Thread {
+ * public Handler mHandler;
+ *
+ * public void run() {
+ * Looper.prepare();
+ *
+ * mHandler = new Handler() {
+ * public void handleMessage(Message msg) {
+ * // process incoming messages here
+ * }
+ * };
+ *
+ * Looper.loop();
+ * }
+ * }</pre>
+ */
+public class Looper {
+ private static final boolean DEBUG = false;
+ private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+
+ // sThreadLocal.get() will return null unless you've called prepare().
+ private static final ThreadLocal sThreadLocal = new ThreadLocal();
+
+ final MessageQueue mQueue;
+ volatile boolean mRun;
+ Thread mThread;
+ private Printer mLogging = null;
+ private static Looper mMainLooper = null;
+
+ /** Initialize the current thread as a looper.
+ * This gives you a chance to create handlers that then reference
+ * this looper, before actually starting the loop. Be sure to call
+ * {@link #loop()} after calling this method, and end it by calling
+ * {@link #quit()}.
+ */
+ public static final void prepare() {
+ if (sThreadLocal.get() != null) {
+ throw new RuntimeException("Only one Looper may be created per thread");
+ }
+ sThreadLocal.set(new Looper());
+ }
+
+ /** Initialize the current thread as a looper, marking it as an application's main
+ * looper. The main looper for your application is created by the Android environment,
+ * so you should never need to call this function yourself.
+ * {@link #prepare()}
+ */
+
+ public static final void prepareMainLooper() {
+ prepare();
+ setMainLooper(myLooper());
+ if (Process.supportsProcesses()) {
+ myLooper().mQueue.mQuitAllowed = false;
+ }
+ }
+
+ private synchronized static void setMainLooper(Looper looper) {
+ mMainLooper = looper;
+ }
+
+ /** Returns the application's main looper, which lives in the main thread of the application.
+ */
+ public synchronized static final Looper getMainLooper() {
+ return mMainLooper;
+ }
+
+ /**
+ * Run the message queue in this thread. Be sure to call
+ * {@link #quit()} to end the loop.
+ */
+ public static final void loop() {
+ Looper me = myLooper();
+ MessageQueue queue = me.mQueue;
+ while (true) {
+ Message msg = queue.next(); // might block
+ //if (!me.mRun) {
+ // break;
+ //}
+ if (msg != null) {
+ if (msg.target == null) {
+ // No target is a magic identifier for the quit message.
+ return;
+ }
+ if (me.mLogging!= null) me.mLogging.println(
+ ">>>>> Dispatching to " + msg.target + " "
+ + msg.callback + ": " + msg.what
+ );
+ msg.target.dispatchMessage(msg);
+ if (me.mLogging!= null) me.mLogging.println(
+ "<<<<< Finished to " + msg.target + " "
+ + msg.callback);
+ msg.recycle();
+ }
+ }
+ }
+
+ /**
+ * Return the Looper object associated with the current thread. Returns
+ * null if the calling thread is not associated with a Looper.
+ */
+ public static final Looper myLooper() {
+ return (Looper)sThreadLocal.get();
+ }
+
+ /**
+ * Control logging of messages as they are processed by this Looper. If
+ * enabled, a log message will be written to <var>printer</var>
+ * at the beginning and ending of each message dispatch, identifying the
+ * target Handler and message contents.
+ *
+ * @param printer A Printer object that will receive log messages, or
+ * null to disable message logging.
+ */
+ public void setMessageLogging(Printer printer) {
+ mLogging = printer;
+ }
+
+ /**
+ * Return the {@link MessageQueue} object associated with the current
+ * thread. This must be called from a thread running a Looper, or a
+ * NullPointerException will be thrown.
+ */
+ public static final MessageQueue myQueue() {
+ return myLooper().mQueue;
+ }
+
+ private Looper() {
+ mQueue = new MessageQueue();
+ mRun = true;
+ mThread = Thread.currentThread();
+ }
+
+ public void quit() {
+ Message msg = Message.obtain();
+ // NOTE: By enqueueing directly into the message queue, the
+ // message is left with a null target. This is how we know it is
+ // a quit message.
+ mQueue.enqueueMessage(msg, 0);
+ }
+
+ /**
+ * Return the Thread associated with this Looper.
+ *
+ * @since CURRENT
+ * {@hide pending API Council approval}
+ */
+ public Thread getThread() {
+ return mThread;
+ }
+
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + this);
+ pw.println(prefix + "mRun=" + mRun);
+ pw.println(prefix + "mThread=" + mThread);
+ pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));
+ if (mQueue != null) {
+ synchronized (mQueue) {
+ Message msg = mQueue.mMessages;
+ int n = 0;
+ while (msg != null) {
+ pw.println(prefix + " Message " + n + ": " + msg);
+ n++;
+ msg = msg.next;
+ }
+ pw.println(prefix + "(Total messages: " + n + ")");
+ }
+ }
+ }
+
+ public String toString() {
+ return "Looper{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + "}";
+ }
+
+ static class HandlerException extends Exception {
+
+ HandlerException(Message message, Throwable cause) {
+ super(createMessage(cause), cause);
+ }
+
+ static String createMessage(Throwable cause) {
+ String causeMsg = cause.getMessage();
+ if (causeMsg == null) {
+ causeMsg = cause.toString();
+ }
+ return causeMsg;
+ }
+ }
+}
+
diff --git a/core/java/android/os/MailboxNotAvailableException.java b/core/java/android/os/MailboxNotAvailableException.java
new file mode 100644
index 0000000..574adbd
--- /dev/null
+++ b/core/java/android/os/MailboxNotAvailableException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/** @hide */
+public class MailboxNotAvailableException extends Throwable
+{
+ /**
+ * This exception represents the case when a request for a
+ * named, published mailbox fails because the requested name has not been published
+ */
+
+ public
+ MailboxNotAvailableException()
+ {
+ }
+
+ public
+ MailboxNotAvailableException(String s)
+ {
+ super(s);
+ }
+}
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
new file mode 100644
index 0000000..76e4f47
--- /dev/null
+++ b/core/java/android/os/MemoryFile.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2008 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 android.os;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+
+/**
+ * MemoryFile is a wrapper for the Linux ashmem driver.
+ * MemoryFiles are backed by shared memory, which can be optionally
+ * set to be purgeable.
+ * Purgeable files may have their contents reclaimed by the kernel
+ * in low memory conditions (only if allowPurging is set to true).
+ * After a file is purged, attempts to read or write the file will
+ * cause an IOException to be thrown.
+ */
+public class MemoryFile
+{
+ private static String TAG = "MemoryFile";
+
+ // returns fd
+ private native int native_open(String name, int length);
+ // returns memory address for ashmem region
+ private native int native_mmap(int fd, int length);
+ private native void native_close(int fd);
+ private native int native_read(int fd, int address, byte[] buffer,
+ int srcOffset, int destOffset, int count, boolean isUnpinned);
+ private native void native_write(int fd, int address, byte[] buffer,
+ int srcOffset, int destOffset, int count, boolean isUnpinned);
+ private native void native_pin(int fd, boolean pin);
+
+ private int mFD; // ashmem file descriptor
+ private int mAddress; // address of ashmem memory
+ private int mLength; // total length of our ashmem region
+ private boolean mAllowPurging = false; // true if our ashmem region is unpinned
+
+ /**
+ * MemoryFile constructor.
+ *
+ * @param name optional name for the file (can be null).
+ * @param length of the memory file in bytes.
+ */
+ public MemoryFile(String name, int length) {
+ mLength = length;
+ mFD = native_open(name, length);
+ mAddress = native_mmap(mFD, length);
+ }
+
+ /**
+ * Closes and releases all resources for the memory file.
+ */
+ public void close() {
+ if (mFD > 0) {
+ native_close(mFD);
+ mFD = 0;
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ if (mFD > 0) {
+ Log.e(TAG, "MemoryFile.finalize() called while ashmem still open");
+ close();
+ }
+ }
+
+ /**
+ * Returns the length of the memory file.
+ *
+ * @return file length.
+ */
+ public int length() {
+ return mLength;
+ }
+
+ /**
+ * Is memory file purging enabled?
+ *
+ * @return true if the file may be purged.
+ */
+ public boolean isPurgingAllowed() {
+ return mAllowPurging;
+ }
+
+ /**
+ * Enables or disables purging of the memory file.
+ *
+ * @param allowPurging true if the operating system can purge the contents
+ * of the file in low memory situations
+ * @return previous value of allowPurging
+ */
+ synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
+ boolean oldValue = mAllowPurging;
+ if (oldValue != allowPurging) {
+ native_pin(mFD, !allowPurging);
+ mAllowPurging = allowPurging;
+ }
+ return oldValue;
+ }
+
+ /**
+ * Creates a new InputStream for reading from the memory file.
+ *
+ @return InputStream
+ */
+ public InputStream getInputStream() {
+ return new MemoryInputStream();
+ }
+
+ /**
+ * Creates a new OutputStream for writing to the memory file.
+ *
+ @return OutputStream
+ */
+ public OutputStream getOutputStream() {
+
+ return new MemoryOutputStream();
+ }
+
+ /**
+ * Reads bytes from the memory file.
+ * Will throw an IOException if the file has been purged.
+ *
+ * @param buffer byte array to read bytes into.
+ * @param srcOffset offset into the memory file to read from.
+ * @param destOffset offset into the byte array buffer to read into.
+ * @param count number of bytes to read.
+ * @return number of bytes read.
+ */
+ public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
+ throws IOException {
+ if (destOffset < 0 || destOffset > buffer.length || count < 0
+ || count > buffer.length - destOffset
+ || srcOffset < 0 || srcOffset > mLength
+ || count > mLength - srcOffset) {
+ throw new IndexOutOfBoundsException();
+ }
+ return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
+ }
+
+ /**
+ * Write bytes to the memory file.
+ * Will throw an IOException if the file has been purged.
+ *
+ * @param buffer byte array to write bytes from.
+ * @param srcOffset offset into the byte array buffer to write from.
+ * @param destOffset offset into the memory file to write to.
+ * @param count number of bytes to write.
+ */
+ public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
+ throws IOException {
+ if (srcOffset < 0 || srcOffset > buffer.length || count < 0
+ || count > buffer.length - srcOffset
+ || destOffset < 0 || destOffset > mLength
+ || count > mLength - destOffset) {
+ throw new IndexOutOfBoundsException();
+ }
+ native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
+ }
+
+ private class MemoryInputStream extends InputStream {
+
+ private int mMark = 0;
+ private int mOffset = 0;
+ private byte[] mSingleByte;
+
+ @Override
+ public int available() throws IOException {
+ if (mOffset >= mLength) {
+ return 0;
+ }
+ return mLength - mOffset;
+ }
+
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public void mark(int readlimit) {
+ mMark = mOffset;
+ }
+
+ @Override
+ public void reset() throws IOException {
+ mOffset = mMark;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (mSingleByte == null) {
+ mSingleByte = new byte[1];
+ }
+ int result = read(mSingleByte, 0, 1);
+ if (result != 1) {
+ throw new IOException("read() failed");
+ }
+ return mSingleByte[0];
+ }
+
+ @Override
+ public int read(byte buffer[], int offset, int count) throws IOException {
+ int result = readBytes(buffer, mOffset, offset, count);
+ if (result > 0) {
+ mOffset += result;
+ }
+ return result;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ if (mOffset + n > mLength) {
+ n = mLength - mOffset;
+ }
+ mOffset += n;
+ return n;
+ }
+ }
+
+ private class MemoryOutputStream extends OutputStream {
+
+ private int mOffset = 0;
+ private byte[] mSingleByte;
+
+ @Override
+ public void write(byte buffer[], int offset, int count) throws IOException {
+ writeBytes(buffer, offset, mOffset, count);
+ }
+
+ @Override
+ public void write(int oneByte) throws IOException {
+ if (mSingleByte == null) {
+ mSingleByte = new byte[1];
+ }
+ mSingleByte[0] = (byte)oneByte;
+ write(mSingleByte, 0, 1);
+ }
+ }
+}
diff --git a/core/java/android/os/Message.aidl b/core/java/android/os/Message.aidl
new file mode 100644
index 0000000..e8dbb5a
--- /dev/null
+++ b/core/java/android/os/Message.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/content/Intent.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable Message;
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
new file mode 100644
index 0000000..4130109
--- /dev/null
+++ b/core/java/android/os/Message.java
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ *
+ * Defines a message containing a description and arbitrary data object that can be
+ * sent to a {@link Handler}. This object contains two extra int fields and an
+ * extra object field that allow you to not do allocations in many cases.
+ *
+ * <p class="note">While the constructor of Message is public, the best way to get
+ * one of these is to call {@link #obtain Message.obtain()} or one of the
+ * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
+ * them from a pool of recycled objects.</p>
+ */
+public final class Message implements Parcelable {
+ /**
+ * User-defined message code so that the recipient can identify
+ * what this message is about. Each {@link Handler} has its own name-space
+ * for message codes, so you do not need to worry about yours conflicting
+ * with other handlers.
+ */
+ public int what;
+
+ // Use these fields instead of using the class's Bundle if you can.
+ /** arg1 and arg2 are lower-cost alternatives to using {@link #setData(Bundle) setData()}
+ if you only need to store a few integer values. */
+ public int arg1;
+
+ /** arg1 and arg2 are lower-cost alternatives to using {@link #setData(Bundle) setData()}
+ if you only need to store a few integer values.*/
+ public int arg2;
+
+ /** An arbitrary object to send to the recipient. This must be null when
+ * sending messages across processes. */
+ public Object obj;
+
+ /** Optional Messenger where replies to this message can be sent.
+ */
+ public Messenger replyTo;
+
+ /*package*/ long when;
+
+ /*package*/ Bundle data;
+
+ /*package*/ Handler target;
+
+ /*package*/ Runnable callback;
+
+ // sometimes we store linked lists of these things
+ /*package*/ Message next;
+
+ private static Object mPoolSync = new Object();
+ private static Message mPool;
+ private static int mPoolSize = 0;
+
+ private static final int MAX_POOL_SIZE = 10;
+
+ /**
+ * Return a new Message instance from the global pool. Allows us to
+ * avoid allocating new objects in many cases.
+ */
+ public static Message obtain() {
+ synchronized (mPoolSync) {
+ if (mPool != null) {
+ Message m = mPool;
+ mPool = m.next;
+ m.next = null;
+ return m;
+ }
+ }
+ return new Message();
+ }
+
+ /**
+ * Same as {@link #obtain()}, but copies the values of an existing
+ * message (including its target) into the new one.
+ * @param orig Original message to copy.
+ * @return A Message object from the global pool.
+ */
+ public static Message obtain(Message orig) {
+ Message m = obtain();
+ m.what = orig.what;
+ m.arg1 = orig.arg1;
+ m.arg2 = orig.arg2;
+ m.obj = orig.obj;
+ m.replyTo = orig.replyTo;
+ if (orig.data != null) {
+ m.data = new Bundle(orig.data);
+ }
+ m.target = orig.target;
+ m.callback = orig.callback;
+
+ return m;
+ }
+
+ /**
+ * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
+ * @param h Handler to assign to the returned Message object's <em>target</em> member.
+ * @return A Message object from the global pool.
+ */
+ public static Message obtain(Handler h) {
+ Message m = obtain();
+ m.target = h;
+
+ return m;
+ }
+
+ /**
+ * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
+ * the Message that is returned.
+ * @param h Handler to assign to the returned Message object's <em>target</em> member.
+ * @param callback Runnable that will execute when the message is handled.
+ * @return A Message object from the global pool.
+ */
+ public static Message obtain(Handler h, Runnable callback) {
+ Message m = obtain();
+ m.target = h;
+ m.callback = callback;
+
+ return m;
+ }
+
+ /**
+ * Same as {@link #obtain()}, but sets the values for both <em>target</em> and
+ * <em>what</em> members on the Message.
+ * @param h Value to assign to the <em>target</em> member.
+ * @param what Value to assign to the <em>what</em> member.
+ * @return A Message object from the global pool.
+ */
+ public static Message obtain(Handler h, int what) {
+ Message m = obtain();
+ m.target = h;
+ m.what = what;
+
+ return m;
+ }
+
+ /**
+ * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
+ * members.
+ * @param h The <em>target</em> value to set.
+ * @param what The <em>what</em> value to set.
+ * @param obj The <em>object</em> method to set.
+ * @return A Message object from the global pool.
+ */
+ public static Message obtain(Handler h, int what, Object obj) {
+ Message m = obtain();
+ m.target = h;
+ m.what = what;
+ m.obj = obj;
+
+ return m;
+ }
+
+ /**
+ * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
+ * <em>arg1</em>, and <em>arg2</em> members.
+ *
+ * @param h The <em>target</em> value to set.
+ * @param what The <em>what</em> value to set.
+ * @param arg1 The <em>arg1</em> value to set.
+ * @param arg2 The <em>arg2</em> value to set.
+ * @return A Message object from the global pool.
+ */
+ public static Message obtain(Handler h, int what, int arg1, int arg2) {
+ Message m = obtain();
+ m.target = h;
+ m.what = what;
+ m.arg1 = arg1;
+ m.arg2 = arg2;
+
+ return m;
+ }
+
+ /**
+ * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
+ * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
+ *
+ * @param h The <em>target</em> value to set.
+ * @param what The <em>what</em> value to set.
+ * @param arg1 The <em>arg1</em> value to set.
+ * @param arg2 The <em>arg2</em> value to set.
+ * @param obj The <em>obj</em> value to set.
+ * @return A Message object from the global pool.
+ */
+ public static Message obtain(Handler h, int what,
+ int arg1, int arg2, Object obj) {
+ Message m = obtain();
+ m.target = h;
+ m.what = what;
+ m.arg1 = arg1;
+ m.arg2 = arg2;
+ m.obj = obj;
+
+ return m;
+ }
+
+ /**
+ * Return a Message instance to the global pool. You MUST NOT touch
+ * the Message after calling this function -- it has effectively been
+ * freed.
+ */
+ public void recycle() {
+ synchronized (mPoolSync) {
+ if (mPoolSize < MAX_POOL_SIZE) {
+ clearForRecycle();
+
+ next = mPool;
+ mPool = this;
+ }
+ }
+ }
+
+ /**
+ * Make this message like o. Performs a shallow copy of the data field.
+ * Does not copy the linked list fields, nor the timestamp or
+ * target/callback of the original message.
+ */
+ public void copyFrom(Message o) {
+ this.what = o.what;
+ this.arg1 = o.arg1;
+ this.arg2 = o.arg2;
+ this.obj = o.obj;
+ this.replyTo = o.replyTo;
+
+ if (o.data != null) {
+ this.data = (Bundle) o.data.clone();
+ } else {
+ this.data = null;
+ }
+ }
+
+ /**
+ * Return the targeted delivery time of this message, in milliseconds.
+ */
+ public long getWhen() {
+ return when;
+ }
+
+ public void setTarget(Handler target) {
+ this.target = target;
+ }
+
+ /**
+ * Retrieve the a {@link android.os.Handler Handler} implementation that
+ * will receive this message. The object must implement
+ * {@link android.os.Handler#handleMessage(android.os.Message)
+ * Handler.handleMessage()}. Each Handler has its own name-space for
+ * message codes, so you do not need to
+ * worry about yours conflicting with other handlers.
+ */
+ public Handler getTarget() {
+ return target;
+ }
+
+ /**
+ * Retrieve callback object that will execute when this message is handled.
+ * This object must implement Runnable. This is called by
+ * the <em>target</em> {@link Handler} that is receiving this Message to
+ * dispatch it. If
+ * not set, the message will be dispatched to the receiving Handler's
+ * {@link Handler#handleMessage(Message Handler.handleMessage())}. */
+ public Runnable getCallback() {
+ return callback;
+ }
+
+ /**
+ * Obtains a Bundle of arbitrary data associated with this
+ * event, lazily creating it if necessary. Set this value by calling {@link #setData(Bundle)}.
+ */
+ public Bundle getData() {
+ if (data == null) {
+ data = new Bundle();
+ }
+
+ return data;
+ }
+
+ /**
+ * Like getData(), but does not lazily create the Bundle. A null
+ * is returned if the Bundle does not already exist.
+ */
+ public Bundle peekData() {
+ return data;
+ }
+
+ /** Sets a Bundle of arbitrary data values. Use arg1 and arg1 members
+ * as a lower cost way to send a few simple integer values, if you can. */
+ public void setData(Bundle data) {
+ this.data = data;
+ }
+
+ /**
+ * Sends this Message to the Handler specified by {@link #getTarget}.
+ * Throws a null pointer exception if this field has not been set.
+ */
+ public void sendToTarget() {
+ target.sendMessage(this);
+ }
+
+ /*package*/ void clearForRecycle() {
+ what = 0;
+ arg1 = 0;
+ arg2 = 0;
+ obj = null;
+ replyTo = null;
+ when = 0;
+ target = null;
+ callback = null;
+ data = null;
+ }
+
+ /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
+ */
+ public Message() {
+ }
+
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+
+ b.append("{ what=");
+ b.append(what);
+
+ b.append(" when=");
+ b.append(when);
+
+ if (arg1 != 0) {
+ b.append(" arg1=");
+ b.append(arg1);
+ }
+
+ if (arg2 != 0) {
+ b.append(" arg2=");
+ b.append(arg2);
+ }
+
+ if (obj != null) {
+ b.append(" obj=");
+ b.append(obj);
+ }
+
+ b.append(" }");
+
+ return b.toString();
+ }
+
+ public static final Parcelable.Creator<Message> CREATOR
+ = new Parcelable.Creator<Message>() {
+ public Message createFromParcel(Parcel source) {
+ Message msg = Message.obtain();
+ msg.readFromParcel(source);
+ return msg;
+ }
+
+ public Message[] newArray(int size) {
+ return new Message[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ if (obj != null || callback != null) {
+ throw new RuntimeException(
+ "Can't marshal objects across processes.");
+ }
+ dest.writeInt(what);
+ dest.writeInt(arg1);
+ dest.writeInt(arg2);
+ dest.writeLong(when);
+ dest.writeBundle(data);
+ Messenger.writeMessengerOrNullToParcel(replyTo, dest);
+ }
+
+ private final void readFromParcel(Parcel source) {
+ what = source.readInt();
+ arg1 = source.readInt();
+ arg2 = source.readInt();
+ when = source.readLong();
+ data = source.readBundle();
+ replyTo = Messenger.readMessengerOrNullFromParcel(source);
+ }
+}
+
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
new file mode 100644
index 0000000..caf0923
--- /dev/null
+++ b/core/java/android/os/MessageQueue.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import java.util.ArrayList;
+
+import android.util.AndroidRuntimeException;
+import android.util.Config;
+import android.util.Log;
+
+import com.android.internal.os.RuntimeInit;
+
+/**
+ * Low-level class holding the list of messages to be dispatched by a
+ * {@link Looper}. Messages are not added directly to a MessageQueue,
+ * but rather through {@link Handler} objects associated with the Looper.
+ *
+ * <p>You can retrieve the MessageQueue for the current thread with
+ * {@link Looper#myQueue() Looper.myQueue()}.
+ */
+public class MessageQueue {
+ Message mMessages;
+ private final ArrayList mIdleHandlers = new ArrayList();
+ private boolean mQuiting = false;
+ boolean mQuitAllowed = true;
+
+ /**
+ * Callback interface for discovering when a thread is going to block
+ * waiting for more messages.
+ */
+ public static interface IdleHandler {
+ /**
+ * Called when the message queue has run out of messages and will now
+ * wait for more. Return true to keep your idle handler active, false
+ * to have it removed. This may be called if there are still messages
+ * pending in the queue, but they are all scheduled to be dispatched
+ * after the current time.
+ */
+ boolean queueIdle();
+ }
+
+ /**
+ * Add a new {@link IdleHandler} to this message queue. This may be
+ * removed automatically for you by returning false from
+ * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
+ * invoked, or explicitly removing it with {@link #removeIdleHandler}.
+ *
+ * <p>This method is safe to call from any thread.
+ *
+ * @param handler The IdleHandler to be added.
+ */
+ public final void addIdleHandler(IdleHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException("Can't add a null IdleHandler");
+ }
+ synchronized (this) {
+ mIdleHandlers.add(handler);
+ }
+ }
+
+ /**
+ * Remove an {@link IdleHandler} from the queue that was previously added
+ * with {@link #addIdleHandler}. If the given object is not currently
+ * in the idle list, nothing is done.
+ *
+ * @param handler The IdleHandler to be removed.
+ */
+ public final void removeIdleHandler(IdleHandler handler) {
+ synchronized (this) {
+ mIdleHandlers.remove(handler);
+ }
+ }
+
+ MessageQueue() {
+ }
+
+ final Message next() {
+ boolean tryIdle = true;
+
+ while (true) {
+ long now;
+ Object[] idlers = null;
+
+ // Try to retrieve the next message, returning if found.
+ synchronized (this) {
+ now = SystemClock.uptimeMillis();
+ Message msg = pullNextLocked(now);
+ if (msg != null) return msg;
+ if (tryIdle && mIdleHandlers.size() > 0) {
+ idlers = mIdleHandlers.toArray();
+ }
+ }
+
+ // There was no message so we are going to wait... but first,
+ // if there are any idle handlers let them know.
+ boolean didIdle = false;
+ if (idlers != null) {
+ for (Object idler : idlers) {
+ boolean keep = false;
+ try {
+ didIdle = true;
+ keep = ((IdleHandler)idler).queueIdle();
+ } catch (Throwable t) {
+ Log.e("MessageQueue",
+ "IdleHandler threw exception", t);
+ RuntimeInit.crash("MessageQueue", t);
+ }
+
+ if (!keep) {
+ synchronized (this) {
+ mIdleHandlers.remove(idler);
+ }
+ }
+ }
+ }
+
+ // While calling an idle handler, a new message could have been
+ // delivered... so go back and look again for a pending message.
+ if (didIdle) {
+ tryIdle = false;
+ continue;
+ }
+
+ synchronized (this) {
+ // No messages, nobody to tell about it... time to wait!
+ try {
+ if (mMessages != null) {
+ if (mMessages.when-now > 0) {
+ Binder.flushPendingCommands();
+ this.wait(mMessages.when-now);
+ }
+ } else {
+ Binder.flushPendingCommands();
+ this.wait();
+ }
+ }
+ catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ final Message pullNextLocked(long now) {
+ Message msg = mMessages;
+ if (msg != null) {
+ if (now >= msg.when) {
+ mMessages = msg.next;
+ if (Config.LOGV) Log.v(
+ "MessageQueue", "Returning message: " + msg);
+ return msg;
+ }
+ }
+
+ return null;
+ }
+
+ final boolean enqueueMessage(Message msg, long when) {
+ if (msg.when != 0) {
+ throw new AndroidRuntimeException(msg
+ + " This message is already in use.");
+ }
+ if (msg.target == null && !mQuitAllowed) {
+ throw new RuntimeException("Main thread not allowed to quit");
+ }
+ synchronized (this) {
+ if (mQuiting) {
+ RuntimeException e = new RuntimeException(
+ msg.target + " sending message to a Handler on a dead thread");
+ Log.w("MessageQueue", e.getMessage(), e);
+ return false;
+ } else if (msg.target == null) {
+ mQuiting = true;
+ }
+
+ msg.when = when;
+ //Log.d("MessageQueue", "Enqueing: " + msg);
+ Message p = mMessages;
+ if (p == null || when == 0 || when < p.when) {
+ msg.next = p;
+ mMessages = msg;
+ this.notify();
+ } else {
+ Message prev = null;
+ while (p != null && p.when <= when) {
+ prev = p;
+ p = p.next;
+ }
+ msg.next = prev.next;
+ prev.next = msg;
+ this.notify();
+ }
+ }
+ return true;
+ }
+
+ final boolean removeMessages(Handler h, int what, Object object,
+ boolean doRemove) {
+ synchronized (this) {
+ Message p = mMessages;
+ boolean found = false;
+
+ // Remove all messages at front.
+ while (p != null && p.target == h && p.what == what
+ && (object == null || p.obj == object)) {
+ if (!doRemove) return true;
+ found = true;
+ Message n = p.next;
+ mMessages = n;
+ p.recycle();
+ p = n;
+ }
+
+ // Remove all messages after front.
+ while (p != null) {
+ Message n = p.next;
+ if (n != null) {
+ if (n.target == h && n.what == what
+ && (object == null || n.obj == object)) {
+ if (!doRemove) return true;
+ found = true;
+ Message nn = n.next;
+ n.recycle();
+ p.next = nn;
+ continue;
+ }
+ }
+ p = n;
+ }
+
+ return found;
+ }
+ }
+
+ final void removeMessages(Handler h, Runnable r, Object object) {
+ if (r == null) {
+ return;
+ }
+
+ synchronized (this) {
+ Message p = mMessages;
+
+ // Remove all messages at front.
+ while (p != null && p.target == h && p.callback == r
+ && (object == null || p.obj == object)) {
+ Message n = p.next;
+ mMessages = n;
+ p.recycle();
+ p = n;
+ }
+
+ // Remove all messages after front.
+ while (p != null) {
+ Message n = p.next;
+ if (n != null) {
+ if (n.target == h && n.callback == r
+ && (object == null || n.obj == object)) {
+ Message nn = n.next;
+ n.recycle();
+ p.next = nn;
+ continue;
+ }
+ }
+ p = n;
+ }
+ }
+ }
+
+ final void removeCallbacksAndMessages(Handler h, Object object) {
+ synchronized (this) {
+ Message p = mMessages;
+
+ // Remove all messages at front.
+ while (p != null && p.target == h
+ && (object == null || p.obj == object)) {
+ Message n = p.next;
+ mMessages = n;
+ p.recycle();
+ p = n;
+ }
+
+ // Remove all messages after front.
+ while (p != null) {
+ Message n = p.next;
+ if (n != null) {
+ if (n.target == h && (object == null || n.obj == object)) {
+ Message nn = n.next;
+ n.recycle();
+ p.next = nn;
+ continue;
+ }
+ }
+ p = n;
+ }
+ }
+ }
+
+ /*
+ private void dumpQueue_l()
+ {
+ Message p = mMessages;
+ System.out.println(this + " queue is:");
+ while (p != null) {
+ System.out.println(" " + p);
+ p = p.next;
+ }
+ }
+ */
+
+ void poke()
+ {
+ synchronized (this) {
+ this.notify();
+ }
+ }
+}
diff --git a/core/java/android/os/Messenger.aidl b/core/java/android/os/Messenger.aidl
new file mode 100644
index 0000000..e6b8886
--- /dev/null
+++ b/core/java/android/os/Messenger.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/content/Intent.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable Messenger;
diff --git a/core/java/android/os/Messenger.java b/core/java/android/os/Messenger.java
new file mode 100644
index 0000000..1bc554e
--- /dev/null
+++ b/core/java/android/os/Messenger.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * Reference to a Handler, which others can use to send messages to it.
+ * This allows for the implementation of message-based communication across
+ * processes, by creating a Messenger pointing to a Handler in one process,
+ * and handing that Messenger to another process.
+ */
+public final class Messenger implements Parcelable {
+ private final IMessenger mTarget;
+
+ /**
+ * Create a new Messenger pointing to the given Handler. Any Message
+ * objects sent through this Messenger will appear in the Handler as if
+ * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
+ * be called directly.
+ *
+ * @param target The Handler that will receive sent messages.
+ */
+ public Messenger(Handler target) {
+ mTarget = target.getIMessenger();
+ }
+
+ /**
+ * Send a Message to this Messenger's Handler.
+ *
+ * @param message The Message to send. Usually retrieved through
+ * {@link Message#obtain() Message.obtain()}.
+ *
+ * @throws RemoteException Throws DeadObjectException if the target
+ * Handler no longer exists.
+ */
+ public void send(Message message) throws RemoteException {
+ mTarget.send(message);
+ }
+
+ /**
+ * Retrieve the IBinder that this Messenger is using to communicate with
+ * its associated Handler.
+ *
+ * @return Returns the IBinder backing this Messenger.
+ */
+ public IBinder getBinder() {
+ return mTarget.asBinder();
+ }
+
+ /**
+ * Comparison operator on two Messenger objects, such that true
+ * is returned then they both point to the same Handler.
+ */
+ public boolean equals(Object otherObj) {
+ if (otherObj == null) {
+ return false;
+ }
+ try {
+ return mTarget.asBinder().equals(((Messenger)otherObj)
+ .mTarget.asBinder());
+ } catch (ClassCastException e) {
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return mTarget.asBinder().hashCode();
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeStrongBinder(mTarget.asBinder());
+ }
+
+ public static final Parcelable.Creator<Messenger> CREATOR
+ = new Parcelable.Creator<Messenger>() {
+ public Messenger createFromParcel(Parcel in) {
+ IBinder target = in.readStrongBinder();
+ return target != null ? new Messenger(target) : null;
+ }
+
+ public Messenger[] newArray(int size) {
+ return new Messenger[size];
+ }
+ };
+
+ /**
+ * Convenience function for writing either a Messenger or null pointer to
+ * a Parcel. You must use this with {@link #readMessengerOrNullFromParcel}
+ * for later reading it.
+ *
+ * @param messenger The Messenger to write, or null.
+ * @param out Where to write the Messenger.
+ */
+ public static void writeMessengerOrNullToParcel(Messenger messenger,
+ Parcel out) {
+ out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
+ : null);
+ }
+
+ /**
+ * Convenience function for reading either a Messenger or null pointer from
+ * a Parcel. You must have previously written the Messenger with
+ * {@link #writeMessengerOrNullToParcel}.
+ *
+ * @param in The Parcel containing the written Messenger.
+ *
+ * @return Returns the Messenger read from the Parcel, or null if null had
+ * been written.
+ */
+ public static Messenger readMessengerOrNullFromParcel(Parcel in) {
+ IBinder b = in.readStrongBinder();
+ return b != null ? new Messenger(b) : null;
+ }
+
+ /**
+ * Create a Messenger from a raw IBinder, which had previously been
+ * retrieved with {@link #getBinder}.
+ *
+ * @param target The IBinder this Messenger should communicate with.
+ */
+ public Messenger(IBinder target) {
+ mTarget = IMessenger.Stub.asInterface(target);
+ }
+}
diff --git a/core/java/android/os/NetStat.java b/core/java/android/os/NetStat.java
new file mode 100644
index 0000000..e294cdf
--- /dev/null
+++ b/core/java/android/os/NetStat.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+
+/** @hide */
+public class NetStat {
+
+ // Logging tag.
+ private final static String TAG = "netstat";
+
+ // We pre-create all the File objects so we don't spend a lot of
+ // CPU at runtime converting from Java Strings to byte[] for the
+ // kernel calls.
+ private final static File[] MOBILE_TX_PACKETS = mobileFiles("tx_packets");
+ private final static File[] MOBILE_RX_PACKETS = mobileFiles("rx_packets");
+ private final static File[] MOBILE_TX_BYTES = mobileFiles("tx_bytes");
+ private final static File[] MOBILE_RX_BYTES = mobileFiles("rx_bytes");
+ private final static File SYS_CLASS_NET_DIR = new File("/sys/class/net");
+
+ /**
+ * Get total number of tx packets sent through rmnet0 or ppp0
+ *
+ * @return number of Tx packets through rmnet0 or ppp0
+ */
+ public static long getMobileTxPkts() {
+ return getMobileStat(MOBILE_TX_PACKETS);
+ }
+
+ /**
+ * Get total number of rx packets received through rmnet0 or ppp0
+ *
+ * @return number of Rx packets through rmnet0 or ppp0
+ */
+ public static long getMobileRxPkts() {
+ return getMobileStat(MOBILE_RX_PACKETS);
+ }
+
+ /**
+ * Get total number of tx bytes received through rmnet0 or ppp0
+ *
+ * @return number of Tx bytes through rmnet0 or ppp0
+ */
+ public static long getMobileTxBytes() {
+ return getMobileStat(MOBILE_TX_BYTES);
+ }
+
+ /**
+ * Get total number of rx bytes received through rmnet0 or ppp0
+ *
+ * @return number of Rx bytes through rmnet0 or ppp0
+ */
+ public static long getMobileRxBytes() {
+ return getMobileStat(MOBILE_RX_BYTES);
+ }
+
+ /**
+ * Get the total number of packets sent through all network interfaces.
+ *
+ * @return the number of packets sent through all network interfaces
+ */
+ public static long getTotalTxPkts() {
+ return getTotalStat("tx_packets");
+ }
+
+ /**
+ * Get the total number of packets received through all network interfaces.
+ *
+ * @return the number of packets received through all network interfaces
+ */
+ public static long getTotalRxPkts() {
+ return getTotalStat("rx_packets");
+ }
+
+ /**
+ * Get the total number of bytes sent through all network interfaces.
+ *
+ * @return the number of bytes sent through all network interfaces
+ */
+ public static long getTotalTxBytes() {
+ return getTotalStat("tx_bytes");
+ }
+
+ /**
+ * Get the total number of bytes received through all network interfaces.
+ *
+ * @return the number of bytes received through all network interfaces
+ */
+ public static long getTotalRxBytes() {
+ return getTotalStat("rx_bytes");
+ }
+
+ /**
+ * Gets network bytes sent for this UID.
+ * The statistics are across all interfaces.
+ * The statistics come from /proc/uid_stat.
+ *
+ * {@see android.os.Process#myUid()}.
+ *
+ * @param uid
+ * @return byte count
+ */
+ public static long getUidTxBytes(int uid) {
+ return getNumberFromFilePath("/proc/uid_stat/" + uid + "/tcp_snd");
+ }
+
+ /**
+ * Gets network bytes received for this UID.
+ * The statistics are across all interfaces.
+ * The statistics come from /proc/uid_stat.
+ *
+ * {@see android.os.Process#myUid()}.
+ *
+ * @param uid
+ * @return byte count
+ */
+ public static long getUidRxBytes(int uid) {
+ return getNumberFromFilePath("/proc/uid_stat/" + uid + "/tcp_rcv");
+ }
+
+ /**
+ * Returns the array of two possible File locations for a given
+ * statistic.
+ */
+ private static File[] mobileFiles(String whatStat) {
+ // Note that we stat them at runtime to see which is
+ // available, rather than here, to guard against the files
+ // coming & going later as modules shut down (e.g. airplane
+ // mode) and whatnot. The runtime stat() isn't expensive compared
+ // to the previous charset conversion that happened before we
+ // were reusing File instances.
+ File[] files = new File[2];
+ files[0] = new File("/sys/class/net/rmnet0/statistics/" + whatStat);
+ files[1] = new File("/sys/class/net/ppp0/statistics/" + whatStat);
+ return files;
+ }
+
+ private static long getTotalStat(String whatStat) {
+ File netdir = new File("/sys/class/net");
+
+ File[] nets = SYS_CLASS_NET_DIR.listFiles();
+ if (nets == null) {
+ return 0;
+ }
+ long total = 0;
+ StringBuffer strbuf = new StringBuffer();
+ for (File net : nets) {
+ strbuf.append(net.getPath()).append(File.separator).append("statistics")
+ .append(File.separator).append(whatStat);
+ total += getNumberFromFilePath(strbuf.toString());
+ strbuf.setLength(0);
+ }
+ return total;
+ }
+
+ private static long getMobileStat(File[] files) {
+ for (int i = 0; i < files.length; i++) {
+ File file = files[i];
+ if (!file.exists()) {
+ continue;
+ }
+ try {
+ RandomAccessFile raf = new RandomAccessFile(file, "r");
+ return getNumberFromFile(raf, file.getAbsolutePath());
+ } catch (IOException e) {
+ Log.w(TAG,
+ "Exception opening TCP statistics file " + file.getAbsolutePath(),
+ e);
+ }
+ }
+ return 0L;
+ }
+
+ // File will have format <number><newline>
+ private static long getNumberFromFilePath(String filename) {
+ RandomAccessFile raf = getFile(filename);
+ if (raf == null) {
+ return 0L;
+ }
+ return getNumberFromFile(raf, filename);
+ }
+
+ // Private buffer for getNumberFromFile. Safe for re-use because
+ // getNumberFromFile is synchronized.
+ private final static byte[] buf = new byte[16];
+
+ private static synchronized long getNumberFromFile(RandomAccessFile raf, String filename) {
+ try {
+ raf.read(buf);
+ raf.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Exception getting TCP bytes from " + filename, e);
+ return 0L;
+ } finally {
+ if (raf != null) {
+ try {
+ raf.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Exception closing " + filename, e);
+ }
+ }
+ }
+
+ long num = 0L;
+ for (int i = 0; i < buf.length; i++) {
+ if (buf[i] < '0' || buf[i] > '9') {
+ break;
+ }
+ num *= 10;
+ num += buf[i] - '0';
+ }
+ return num;
+ }
+
+ private static RandomAccessFile getFile(String filename) {
+ File f = new File(filename);
+ if (!f.canRead()) {
+ return null;
+ }
+
+ try {
+ return new RandomAccessFile(f, "r");
+ } catch (IOException e) {
+ Log.w(TAG, "Exception opening TCP statistics file " + filename, e);
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
new file mode 100644
index 0000000..9a71f6e
--- /dev/null
+++ b/core/java/android/os/Parcel.java
@@ -0,0 +1,2051 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Container for a message (data and object references) that can
+ * be sent through an IBinder. A Parcel can contain both flattened data
+ * that will be unflattened on the other side of the IPC (using the various
+ * methods here for writing specific types, or the general
+ * {@link Parcelable} interface), and references to live {@link IBinder}
+ * objects that will result in the other side receiving a proxy IBinder
+ * connected with the original IBinder in the Parcel.
+ *
+ * <p class="note">Parcel is <strong>not</strong> a general-purpose
+ * serialization mechanism. This class (and the corresponding
+ * {@link Parcelable} API for placing arbitrary objects into a Parcel) is
+ * designed as a high-performance IPC transport. As such, it is not
+ * appropriate to place any Parcel data in to persistent storage: changes
+ * in the underlying implementation of any of the data in the Parcel can
+ * render older data unreadable.</p>
+ *
+ * <p>The bulk of the Parcel API revolves around reading and writing data
+ * of various types. There are six major classes of such functions available.</p>
+ *
+ * <h3>Primitives</h3>
+ *
+ * <p>The most basic data functions are for writing and reading primitive
+ * data types: {@link #writeByte}, {@link #readByte}, {@link #writeDouble},
+ * {@link #readDouble}, {@link #writeFloat}, {@link #readFloat}, {@link #writeInt},
+ * {@link #readInt}, {@link #writeLong}, {@link #readLong},
+ * {@link #writeString}, {@link #readString}. Most other
+ * data operations are built on top of these. The given data is written and
+ * read using the endianess of the host CPU.</p>
+ *
+ * <h3>Primitive Arrays</h3>
+ *
+ * <p>There are a variety of methods for reading and writing raw arrays
+ * of primitive objects, which generally result in writing a 4-byte length
+ * followed by the primitive data items. The methods for reading can either
+ * read the data into an existing array, or create and return a new array.
+ * These available types are:</p>
+ *
+ * <ul>
+ * <li> {@link #writeBooleanArray(boolean[])},
+ * {@link #readBooleanArray(boolean[])}, {@link #createBooleanArray()}
+ * <li> {@link #writeByteArray(byte[])},
+ * {@link #writeByteArray(byte[], int, int)}, {@link #readByteArray(byte[])},
+ * {@link #createByteArray()}
+ * <li> {@link #writeCharArray(char[])}, {@link #readCharArray(char[])},
+ * {@link #createCharArray()}
+ * <li> {@link #writeDoubleArray(double[])}, {@link #readDoubleArray(double[])},
+ * {@link #createDoubleArray()}
+ * <li> {@link #writeFloatArray(float[])}, {@link #readFloatArray(float[])},
+ * {@link #createFloatArray()}
+ * <li> {@link #writeIntArray(int[])}, {@link #readIntArray(int[])},
+ * {@link #createIntArray()}
+ * <li> {@link #writeLongArray(long[])}, {@link #readLongArray(long[])},
+ * {@link #createLongArray()}
+ * <li> {@link #writeStringArray(String[])}, {@link #readStringArray(String[])},
+ * {@link #createStringArray()}.
+ * <li> {@link #writeSparseBooleanArray(SparseBooleanArray)},
+ * {@link #readSparseBooleanArray()}.
+ * </ul>
+ *
+ * <h3>Parcelables</h3>
+ *
+ * <p>The {@link Parcelable} protocol provides an extremely efficient (but
+ * low-level) protocol for objects to write and read themselves from Parcels.
+ * You can use the direct methods {@link #writeParcelable(Parcelable, int)}
+ * and {@link #readParcelable(ClassLoader)} or
+ * {@link #writeParcelableArray} and
+ * {@link #readParcelableArray(ClassLoader)} to write or read. These
+ * methods write both the class type and its data to the Parcel, allowing
+ * that class to be reconstructed from the appropriate class loader when
+ * later reading.</p>
+ *
+ * <p>There are also some methods that provide a more efficient way to work
+ * with Parcelables: {@link #writeTypedArray},
+ * {@link #writeTypedList(List)},
+ * {@link #readTypedArray} and {@link #readTypedList}. These methods
+ * do not write the class information of the original object: instead, the
+ * caller of the read function must know what type to expect and pass in the
+ * appropriate {@link Parcelable.Creator Parcelable.Creator} instead to
+ * properly construct the new object and read its data. (To more efficient
+ * write and read a single Parceable object, you can directly call
+ * {@link Parcelable#writeToParcel Parcelable.writeToParcel} and
+ * {@link Parcelable.Creator#createFromParcel Parcelable.Creator.createFromParcel}
+ * yourself.)</p>
+ *
+ * <h3>Bundles</h3>
+ *
+ * <p>A special type-safe container, called {@link Bundle}, is available
+ * for key/value maps of heterogeneous values. This has many optimizations
+ * for improved performance when reading and writing data, and its type-safe
+ * API avoids difficult to debug type errors when finally marshalling the
+ * data contents into a Parcel. The methods to use are
+ * {@link #writeBundle(Bundle)}, {@link #readBundle()}, and
+ * {@link #readBundle(ClassLoader)}.
+ *
+ * <h3>Active Objects</h3>
+ *
+ * <p>An unusual feature of Parcel is the ability to read and write active
+ * objects. For these objects the actual contents of the object is not
+ * written, rather a special token referencing the object is written. When
+ * reading the object back from the Parcel, you do not get a new instance of
+ * the object, but rather a handle that operates on the exact same object that
+ * was originally written. There are two forms of active objects available.</p>
+ *
+ * <p>{@link Binder} objects are a core facility of Android's general cross-process
+ * communication system. The {@link IBinder} interface describes an abstract
+ * protocol with a Binder object. Any such interface can be written in to
+ * a Parcel, and upon reading you will receive either the original object
+ * implementing that interface or a special proxy implementation
+ * that communicates calls back to the original object. The methods to use are
+ * {@link #writeStrongBinder(IBinder)},
+ * {@link #writeStrongInterface(IInterface)}, {@link #readStrongBinder()},
+ * {@link #writeBinderArray(IBinder[])}, {@link #readBinderArray(IBinder[])},
+ * {@link #createBinderArray()},
+ * {@link #writeBinderList(List)}, {@link #readBinderList(List)},
+ * {@link #createBinderArrayList()}.</p>
+ *
+ * <p>FileDescriptor objects, representing raw Linux file descriptor identifiers,
+ * can be written and {@link ParcelFileDescriptor} objects returned to operate
+ * on the original file descriptor. The returned file descriptor is a dup
+ * of the original file descriptor: the object and fd is different, but
+ * operating on the same underlying file stream, with the same position, etc.
+ * The methods to use are {@link #writeFileDescriptor(FileDescriptor)},
+ * {@link #readFileDescriptor()}.
+ *
+ * <h3>Untyped Containers</h3>
+ *
+ * <p>A final class of methods are for writing and reading standard Java
+ * containers of arbitrary types. These all revolve around the
+ * {@link #writeValue(Object)} and {@link #readValue(ClassLoader)} methods
+ * which define the types of objects allowed. The container methods are
+ * {@link #writeArray(Object[])}, {@link #readArray(ClassLoader)},
+ * {@link #writeList(List)}, {@link #readList(List, ClassLoader)},
+ * {@link #readArrayList(ClassLoader)},
+ * {@link #writeMap(Map)}, {@link #readMap(Map, ClassLoader)},
+ * {@link #writeSparseArray(SparseArray)},
+ * {@link #readSparseArray(ClassLoader)}.
+ */
+public final class Parcel {
+ private static final boolean DEBUG_RECYCLE = false;
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ private int mObject; // used by native code
+ @SuppressWarnings({"UnusedDeclaration"})
+ private int mOwnObject; // used by native code
+ private RuntimeException mStack;
+
+ private static final int POOL_SIZE = 6;
+ private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
+ private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
+
+ private static final int VAL_NULL = -1;
+ private static final int VAL_STRING = 0;
+ private static final int VAL_INTEGER = 1;
+ private static final int VAL_MAP = 2;
+ private static final int VAL_BUNDLE = 3;
+ private static final int VAL_PARCELABLE = 4;
+ private static final int VAL_SHORT = 5;
+ private static final int VAL_LONG = 6;
+ private static final int VAL_FLOAT = 7;
+ private static final int VAL_DOUBLE = 8;
+ private static final int VAL_BOOLEAN = 9;
+ private static final int VAL_CHARSEQUENCE = 10;
+ private static final int VAL_LIST = 11;
+ private static final int VAL_SPARSEARRAY = 12;
+ private static final int VAL_BYTEARRAY = 13;
+ private static final int VAL_STRINGARRAY = 14;
+ private static final int VAL_IBINDER = 15;
+ private static final int VAL_PARCELABLEARRAY = 16;
+ private static final int VAL_OBJECTARRAY = 17;
+ private static final int VAL_INTARRAY = 18;
+ private static final int VAL_LONGARRAY = 19;
+ private static final int VAL_BYTE = 20;
+ private static final int VAL_SERIALIZABLE = 21;
+ private static final int VAL_SPARSEBOOLEANARRAY = 22;
+ private static final int VAL_BOOLEANARRAY = 23;
+
+ private static final int EX_SECURITY = -1;
+ private static final int EX_BAD_PARCELABLE = -2;
+ private static final int EX_ILLEGAL_ARGUMENT = -3;
+ private static final int EX_NULL_POINTER = -4;
+ private static final int EX_ILLEGAL_STATE = -5;
+
+ public final static Parcelable.Creator<String> STRING_CREATOR
+ = new Parcelable.Creator<String>() {
+ public String createFromParcel(Parcel source) {
+ return source.readString();
+ }
+ public String[] newArray(int size) {
+ return new String[size];
+ }
+ };
+
+ /**
+ * Retrieve a new Parcel object from the pool.
+ */
+ public static Parcel obtain() {
+ final Parcel[] pool = sOwnedPool;
+ synchronized (pool) {
+ Parcel p;
+ for (int i=0; i<POOL_SIZE; i++) {
+ p = pool[i];
+ if (p != null) {
+ pool[i] = null;
+ if (DEBUG_RECYCLE) {
+ p.mStack = new RuntimeException();
+ }
+ return p;
+ }
+ }
+ }
+ return new Parcel(0);
+ }
+
+ /**
+ * Put a Parcel object back into the pool. You must not touch
+ * the object after this call.
+ */
+ public final void recycle() {
+ if (DEBUG_RECYCLE) mStack = null;
+ freeBuffer();
+ final Parcel[] pool = mOwnObject != 0 ? sOwnedPool : sHolderPool;
+ synchronized (pool) {
+ for (int i=0; i<POOL_SIZE; i++) {
+ if (pool[i] == null) {
+ pool[i] = this;
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the total amount of data contained in the parcel.
+ */
+ public final native int dataSize();
+
+ /**
+ * Returns the amount of data remaining to be read from the
+ * parcel. That is, {@link #dataSize}-{@link #dataPosition}.
+ */
+ public final native int dataAvail();
+
+ /**
+ * Returns the current position in the parcel data. Never
+ * more than {@link #dataSize}.
+ */
+ public final native int dataPosition();
+
+ /**
+ * Returns the total amount of space in the parcel. This is always
+ * >= {@link #dataSize}. The difference between it and dataSize() is the
+ * amount of room left until the parcel needs to re-allocate its
+ * data buffer.
+ */
+ public final native int dataCapacity();
+
+ /**
+ * Change the amount of data in the parcel. Can be either smaller or
+ * larger than the current size. If larger than the current capacity,
+ * more memory will be allocated.
+ *
+ * @param size The new number of bytes in the Parcel.
+ */
+ public final native void setDataSize(int size);
+
+ /**
+ * Move the current read/write position in the parcel.
+ * @param pos New offset in the parcel; must be between 0 and
+ * {@link #dataSize}.
+ */
+ public final native void setDataPosition(int pos);
+
+ /**
+ * Change the capacity (current available space) of the parcel.
+ *
+ * @param size The new capacity of the parcel, in bytes. Can not be
+ * less than {@link #dataSize} -- that is, you can not drop existing data
+ * with this method.
+ */
+ public final native void setDataCapacity(int size);
+
+ /**
+ * Returns the raw bytes of the parcel.
+ *
+ * <p class="note">The data you retrieve here <strong>must not</strong>
+ * be placed in any kind of persistent storage (on local disk, across
+ * a network, etc). For that, you should use standard serialization
+ * or another kind of general serialization mechanism. The Parcel
+ * marshalled representation is highly optimized for local IPC, and as
+ * such does not attempt to maintain compatibility with data created
+ * in different versions of the platform.
+ */
+ public final native byte[] marshall();
+
+ /**
+ * Set the bytes in data to be the raw bytes of this Parcel.
+ */
+ public final native void unmarshall(byte[] data, int offest, int length);
+
+ public final native void appendFrom(Parcel parcel, int offset, int length);
+
+ /**
+ * Report whether the parcel contains any marshalled file descriptors.
+ */
+ public final native boolean hasFileDescriptors();
+
+ /**
+ * Store or read an IBinder interface token in the parcel at the current
+ * {@link #dataPosition}. This is used to validate that the marshalled
+ * transaction is intended for the target interface.
+ */
+ public final native void writeInterfaceToken(String interfaceName);
+ public final native void enforceInterface(String interfaceName);
+
+ /**
+ * Write a byte array into the parcel at the current {#link #dataPosition},
+ * growing {@link #dataCapacity} if needed.
+ * @param b Bytes to place into the parcel.
+ */
+ public final void writeByteArray(byte[] b) {
+ writeByteArray(b, 0, (b != null) ? b.length : 0);
+ }
+
+ /**
+ * Write an byte array into the parcel at the current {#link #dataPosition},
+ * growing {@link #dataCapacity} if needed.
+ * @param b Bytes to place into the parcel.
+ * @param offset Index of first byte to be written.
+ * @param len Number of bytes to write.
+ */
+ public final void writeByteArray(byte[] b, int offset, int len) {
+ if (b == null) {
+ writeInt(-1);
+ return;
+ }
+ if (b.length < offset + len || len < 0 || offset < 0) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ writeNative(b, offset, len);
+ }
+
+ private native void writeNative(byte[] b, int offset, int len);
+
+ /**
+ * Write an integer value into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed.
+ */
+ public final native void writeInt(int val);
+
+ /**
+ * Write a long integer value into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed.
+ */
+ public final native void writeLong(long val);
+
+ /**
+ * Write a floating point value into the parcel at the current
+ * dataPosition(), growing dataCapacity() if needed.
+ */
+ public final native void writeFloat(float val);
+
+ /**
+ * Write a double precision floating point value into the parcel at the
+ * current dataPosition(), growing dataCapacity() if needed.
+ */
+ public final native void writeDouble(double val);
+
+ /**
+ * Write a string value into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed.
+ */
+ public final native void writeString(String val);
+
+ /**
+ * Write an object into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed.
+ */
+ public final native void writeStrongBinder(IBinder val);
+
+ /**
+ * Write an object into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed.
+ */
+ public final void writeStrongInterface(IInterface val) {
+ writeStrongBinder(val == null ? null : val.asBinder());
+ }
+
+ /**
+ * Write a FileDescriptor into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed.
+ */
+ public final native void writeFileDescriptor(FileDescriptor val);
+
+ /**
+ * Write an byte value into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed.
+ */
+ public final void writeByte(byte val) {
+ writeInt(val);
+ }
+
+ /**
+ * Please use {@link #writeBundle} instead. Flattens a Map into the parcel
+ * at the current dataPosition(),
+ * growing dataCapacity() if needed. The Map keys must be String objects.
+ * The Map values are written using {@link #writeValue} and must follow
+ * the specification there.
+ *
+ * <p>It is strongly recommended to use {@link #writeBundle} instead of
+ * this method, since the Bundle class provides a type-safe API that
+ * allows you to avoid mysterious type errors at the point of marshalling.
+ */
+ public final void writeMap(Map val) {
+ writeMapInternal((Map<String,Object>) val);
+ }
+
+ /**
+ * Flatten a Map into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed. The Map keys must be String objects.
+ */
+ private void writeMapInternal(Map<String,Object> val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ Set<Map.Entry<String,Object>> entries = val.entrySet();
+ writeInt(entries.size());
+ for (Map.Entry<String,Object> e : entries) {
+ writeValue(e.getKey());
+ writeValue(e.getValue());
+ }
+ }
+
+ /**
+ * Flatten a Bundle into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed.
+ */
+ public final void writeBundle(Bundle val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+
+ if (val.mParcelledData != null) {
+ int length = val.mParcelledData.dataSize();
+ appendFrom(val.mParcelledData, 0, length);
+ } else {
+ writeInt(-1); // dummy, will hold length
+ int oldPos = dataPosition();
+ writeInt(0x4C444E42); // 'B' 'N' 'D' 'L'
+
+ writeMapInternal(val.mMap);
+ int newPos = dataPosition();
+
+ // Backpatch length
+ setDataPosition(oldPos - 4);
+ int length = newPos - oldPos;
+ writeInt(length);
+ setDataPosition(newPos);
+ }
+ }
+
+ /**
+ * Flatten a List into the parcel at the current dataPosition(), growing
+ * dataCapacity() if needed. The List values are written using
+ * {@link #writeValue} and must follow the specification there.
+ */
+ public final void writeList(List val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ int N = val.size();
+ int i=0;
+ writeInt(N);
+ while (i < N) {
+ writeValue(val.get(i));
+ i++;
+ }
+ }
+
+ /**
+ * Flatten an Object array into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed. The array values are written using
+ * {@link #writeValue} and must follow the specification there.
+ */
+ public final void writeArray(Object[] val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ int N = val.length;
+ int i=0;
+ writeInt(N);
+ while (i < N) {
+ writeValue(val[i]);
+ i++;
+ }
+ }
+
+ /**
+ * Flatten a generic SparseArray into the parcel at the current
+ * dataPosition(), growing dataCapacity() if needed. The SparseArray
+ * values are written using {@link #writeValue} and must follow the
+ * specification there.
+ */
+ public final void writeSparseArray(SparseArray<Object> val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ int N = val.size();
+ writeInt(N);
+ int i=0;
+ while (i < N) {
+ writeInt(val.keyAt(i));
+ writeValue(val.valueAt(i));
+ i++;
+ }
+ }
+
+ public final void writeSparseBooleanArray(SparseBooleanArray val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ int N = val.size();
+ writeInt(N);
+ int i=0;
+ while (i < N) {
+ writeInt(val.keyAt(i));
+ writeByte((byte)(val.valueAt(i) ? 1 : 0));
+ i++;
+ }
+ }
+
+ public final void writeBooleanArray(boolean[] val) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeInt(val[i] ? 1 : 0);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ public final boolean[] createBooleanArray() {
+ int N = readInt();
+ // >>2 as a fast divide-by-4 works in the create*Array() functions
+ // because dataAvail() will never return a negative number. 4 is
+ // the size of a stored boolean in the stream.
+ if (N >= 0 && N <= (dataAvail() >> 2)) {
+ boolean[] val = new boolean[N];
+ for (int i=0; i<N; i++) {
+ val[i] = readInt() != 0;
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ public final void readBooleanArray(boolean[] val) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = readInt() != 0;
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+ public final void writeCharArray(char[] val) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeInt((int)val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ public final char[] createCharArray() {
+ int N = readInt();
+ if (N >= 0 && N <= (dataAvail() >> 2)) {
+ char[] val = new char[N];
+ for (int i=0; i<N; i++) {
+ val[i] = (char)readInt();
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ public final void readCharArray(char[] val) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = (char)readInt();
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+ public final void writeIntArray(int[] val) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeInt(val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ public final int[] createIntArray() {
+ int N = readInt();
+ if (N >= 0 && N <= (dataAvail() >> 2)) {
+ int[] val = new int[N];
+ for (int i=0; i<N; i++) {
+ val[i] = readInt();
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ public final void readIntArray(int[] val) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = readInt();
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+ public final void writeLongArray(long[] val) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeLong(val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ public final long[] createLongArray() {
+ int N = readInt();
+ // >>3 because stored longs are 64 bits
+ if (N >= 0 && N <= (dataAvail() >> 3)) {
+ long[] val = new long[N];
+ for (int i=0; i<N; i++) {
+ val[i] = readLong();
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ public final void readLongArray(long[] val) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = readLong();
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+ public final void writeFloatArray(float[] val) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeFloat(val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ public final float[] createFloatArray() {
+ int N = readInt();
+ // >>2 because stored floats are 4 bytes
+ if (N >= 0 && N <= (dataAvail() >> 2)) {
+ float[] val = new float[N];
+ for (int i=0; i<N; i++) {
+ val[i] = readFloat();
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ public final void readFloatArray(float[] val) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = readFloat();
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+ public final void writeDoubleArray(double[] val) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeDouble(val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ public final double[] createDoubleArray() {
+ int N = readInt();
+ // >>3 because stored doubles are 8 bytes
+ if (N >= 0 && N <= (dataAvail() >> 3)) {
+ double[] val = new double[N];
+ for (int i=0; i<N; i++) {
+ val[i] = readDouble();
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ public final void readDoubleArray(double[] val) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = readDouble();
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+ public final void writeStringArray(String[] val) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeString(val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ public final String[] createStringArray() {
+ int N = readInt();
+ if (N >= 0) {
+ String[] val = new String[N];
+ for (int i=0; i<N; i++) {
+ val[i] = readString();
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ public final void readStringArray(String[] val) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = readString();
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+ public final void writeBinderArray(IBinder[] val) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeStrongBinder(val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ public final IBinder[] createBinderArray() {
+ int N = readInt();
+ if (N >= 0) {
+ IBinder[] val = new IBinder[N];
+ for (int i=0; i<N; i++) {
+ val[i] = readStrongBinder();
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ public final void readBinderArray(IBinder[] val) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = readStrongBinder();
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+ /**
+ * Flatten a List containing a particular object type into the parcel, at
+ * the current dataPosition() and growing dataCapacity() if needed. The
+ * type of the objects in the list must be one that implements Parcelable.
+ * Unlike the generic writeList() method, however, only the raw data of the
+ * objects is written and not their type, so you must use the corresponding
+ * readTypedList() to unmarshall them.
+ *
+ * @param val The list of objects to be written.
+ *
+ * @see #createTypedArrayList
+ * @see #readTypedList
+ * @see Parcelable
+ */
+ public final <T extends Parcelable> void writeTypedList(List<T> val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ int N = val.size();
+ int i=0;
+ writeInt(N);
+ while (i < N) {
+ T item = val.get(i);
+ if (item != null) {
+ writeInt(1);
+ item.writeToParcel(this, 0);
+ } else {
+ writeInt(0);
+ }
+ i++;
+ }
+ }
+
+ /**
+ * Flatten a List containing String objects into the parcel, at
+ * the current dataPosition() and growing dataCapacity() if needed. They
+ * can later be retrieved with {@link #createStringArrayList} or
+ * {@link #readStringList}.
+ *
+ * @param val The list of strings to be written.
+ *
+ * @see #createStringArrayList
+ * @see #readStringList
+ */
+ public final void writeStringList(List<String> val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ int N = val.size();
+ int i=0;
+ writeInt(N);
+ while (i < N) {
+ writeString(val.get(i));
+ i++;
+ }
+ }
+
+ /**
+ * Flatten a List containing IBinder objects into the parcel, at
+ * the current dataPosition() and growing dataCapacity() if needed. They
+ * can later be retrieved with {@link #createBinderArrayList} or
+ * {@link #readBinderList}.
+ *
+ * @param val The list of strings to be written.
+ *
+ * @see #createBinderArrayList
+ * @see #readBinderList
+ */
+ public final void writeBinderList(List<IBinder> val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ int N = val.size();
+ int i=0;
+ writeInt(N);
+ while (i < N) {
+ writeStrongBinder(val.get(i));
+ i++;
+ }
+ }
+
+ /**
+ * Flatten a heterogeneous array containing a particular object type into
+ * the parcel, at
+ * the current dataPosition() and growing dataCapacity() if needed. The
+ * type of the objects in the array must be one that implements Parcelable.
+ * Unlike the {@link #writeParcelableArray} method, however, only the
+ * raw data of the objects is written and not their type, so you must use
+ * {@link #readTypedArray} with the correct corresponding
+ * {@link Parcelable.Creator} implementation to unmarshall them.
+ *
+ * @param val The array of objects to be written.
+ * @param parcelableFlags Contextual flags as per
+ * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+ *
+ * @see #readTypedArray
+ * @see #writeParcelableArray
+ * @see Parcelable.Creator
+ */
+ public final <T extends Parcelable> void writeTypedArray(T[] val,
+ int parcelableFlags) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ T item = val[i];
+ if (item != null) {
+ writeInt(1);
+ item.writeToParcel(this, parcelableFlags);
+ } else {
+ writeInt(0);
+ }
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ /**
+ * Flatten a generic object in to a parcel. The given Object value may
+ * currently be one of the following types:
+ *
+ * <ul>
+ * <li> null
+ * <li> String
+ * <li> Byte
+ * <li> Short
+ * <li> Integer
+ * <li> Long
+ * <li> Float
+ * <li> Double
+ * <li> Boolean
+ * <li> String[]
+ * <li> boolean[]
+ * <li> byte[]
+ * <li> int[]
+ * <li> long[]
+ * <li> Object[] (supporting objects of the same type defined here).
+ * <li> {@link Bundle}
+ * <li> Map (as supported by {@link #writeMap}).
+ * <li> Any object that implements the {@link Parcelable} protocol.
+ * <li> Parcelable[]
+ * <li> CharSequence (as supported by {@link TextUtils#writeToParcel}).
+ * <li> List (as supported by {@link #writeList}).
+ * <li> {@link SparseArray} (as supported by {@link #writeSparseArray}).
+ * <li> {@link IBinder}
+ * <li> Any object that implements Serializable (but see
+ * {@link #writeSerializable} for caveats). Note that all of the
+ * previous types have relatively efficient implementations for
+ * writing to a Parcel; having to rely on the generic serialization
+ * approach is much less efficient and should be avoided whenever
+ * possible.
+ * </ul>
+ */
+ public final void writeValue(Object v) {
+ if (v == null) {
+ writeInt(VAL_NULL);
+ } else if (v instanceof String) {
+ writeInt(VAL_STRING);
+ writeString((String) v);
+ } else if (v instanceof Integer) {
+ writeInt(VAL_INTEGER);
+ writeInt((Integer) v);
+ } else if (v instanceof Map) {
+ writeInt(VAL_MAP);
+ writeMap((Map) v);
+ } else if (v instanceof Bundle) {
+ // Must be before Parcelable
+ writeInt(VAL_BUNDLE);
+ writeBundle((Bundle) v);
+ } else if (v instanceof Parcelable) {
+ writeInt(VAL_PARCELABLE);
+ writeParcelable((Parcelable) v, 0);
+ } else if (v instanceof Short) {
+ writeInt(VAL_SHORT);
+ writeInt(((Short) v).intValue());
+ } else if (v instanceof Long) {
+ writeInt(VAL_LONG);
+ writeLong((Long) v);
+ } else if (v instanceof Float) {
+ writeInt(VAL_FLOAT);
+ writeFloat((Float) v);
+ } else if (v instanceof Double) {
+ writeInt(VAL_DOUBLE);
+ writeDouble((Double) v);
+ } else if (v instanceof Boolean) {
+ writeInt(VAL_BOOLEAN);
+ writeInt((Boolean) v ? 1 : 0);
+ } else if (v instanceof CharSequence) {
+ // Must be after String
+ writeInt(VAL_CHARSEQUENCE);
+ TextUtils.writeToParcel((CharSequence) v, this, 0);
+ } else if (v instanceof List) {
+ writeInt(VAL_LIST);
+ writeList((List) v);
+ } else if (v instanceof SparseArray) {
+ writeInt(VAL_SPARSEARRAY);
+ writeSparseArray((SparseArray) v);
+ } else if (v instanceof boolean[]) {
+ writeInt(VAL_BOOLEANARRAY);
+ writeBooleanArray((boolean[]) v);
+ } else if (v instanceof byte[]) {
+ writeInt(VAL_BYTEARRAY);
+ writeByteArray((byte[]) v);
+ } else if (v instanceof String[]) {
+ writeInt(VAL_STRINGARRAY);
+ writeStringArray((String[]) v);
+ } else if (v instanceof IBinder) {
+ writeInt(VAL_IBINDER);
+ writeStrongBinder((IBinder) v);
+ } else if (v instanceof Parcelable[]) {
+ writeInt(VAL_PARCELABLEARRAY);
+ writeParcelableArray((Parcelable[]) v, 0);
+ } else if (v instanceof Object[]) {
+ writeInt(VAL_OBJECTARRAY);
+ writeArray((Object[]) v);
+ } else if (v instanceof int[]) {
+ writeInt(VAL_INTARRAY);
+ writeIntArray((int[]) v);
+ } else if (v instanceof long[]) {
+ writeInt(VAL_LONGARRAY);
+ writeLongArray((long[]) v);
+ } else if (v instanceof Byte) {
+ writeInt(VAL_BYTE);
+ writeInt((Byte) v);
+ } else if (v instanceof Serializable) {
+ // Must be last
+ writeInt(VAL_SERIALIZABLE);
+ writeSerializable((Serializable) v);
+ } else {
+ throw new RuntimeException("Parcel: unable to marshal value " + v);
+ }
+ }
+
+ /**
+ * Flatten the name of the class of the Parcelable and its contents
+ * into the parcel.
+ *
+ * @param p The Parcelable object to be written.
+ * @param parcelableFlags Contextual flags as per
+ * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+ */
+ public final void writeParcelable(Parcelable p, int parcelableFlags) {
+ if (p == null) {
+ writeString(null);
+ return;
+ }
+ String name = p.getClass().getName();
+ writeString(name);
+ p.writeToParcel(this, parcelableFlags);
+ }
+
+ /**
+ * Write a generic serializable object in to a Parcel. It is strongly
+ * recommended that this method be avoided, since the serialization
+ * overhead is extremely large, and this approach will be much slower than
+ * using the other approaches to writing data in to a Parcel.
+ */
+ public final void writeSerializable(Serializable s) {
+ if (s == null) {
+ writeString(null);
+ return;
+ }
+ String name = s.getClass().getName();
+ writeString(name);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(s);
+ oos.close();
+
+ writeByteArray(baos.toByteArray());
+ } catch (IOException ioe) {
+ throw new RuntimeException("Parcelable encountered " +
+ "IOException writing serializable object (name = " + name +
+ ")", ioe);
+ }
+ }
+
+ /**
+ * Special function for writing an exception result at the header of
+ * a parcel, to be used when returning an exception from a transaction.
+ * Note that this currently only supports a few exception types; any other
+ * exception will be re-thrown by this function as a RuntimeException
+ * (to be caught by the system's last-resort exception handling when
+ * dispatching a transaction).
+ *
+ * <p>The supported exception types are:
+ * <ul>
+ * <li>{@link BadParcelableException}
+ * <li>{@link IllegalArgumentException}
+ * <li>{@link IllegalStateException}
+ * <li>{@link NullPointerException}
+ * <li>{@link SecurityException}
+ * </ul>
+ *
+ * @param e The Exception to be written.
+ *
+ * @see #writeNoException
+ * @see #readException
+ */
+ public final void writeException(Exception e) {
+ int code = 0;
+ if (e instanceof SecurityException) {
+ code = EX_SECURITY;
+ } else if (e instanceof BadParcelableException) {
+ code = EX_BAD_PARCELABLE;
+ } else if (e instanceof IllegalArgumentException) {
+ code = EX_ILLEGAL_ARGUMENT;
+ } else if (e instanceof NullPointerException) {
+ code = EX_NULL_POINTER;
+ } else if (e instanceof IllegalStateException) {
+ code = EX_ILLEGAL_STATE;
+ }
+ writeInt(code);
+ if (code == 0) {
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ }
+ throw new RuntimeException(e);
+ }
+ writeString(e.getMessage());
+ }
+
+ /**
+ * Special function for writing information at the front of the Parcel
+ * indicating that no exception occurred.
+ *
+ * @see #writeException
+ * @see #readException
+ */
+ public final void writeNoException() {
+ writeInt(0);
+ }
+
+ /**
+ * Special function for reading an exception result from the header of
+ * a parcel, to be used after receiving the result of a transaction. This
+ * will throw the exception for you if it had been written to the Parcel,
+ * otherwise return and let you read the normal result data from the Parcel.
+ *
+ * @see #writeException
+ * @see #writeNoException
+ */
+ public final void readException() {
+ int code = readInt();
+ if (code == 0) return;
+ String msg = readString();
+ readException(code, msg);
+ }
+
+ /**
+ * Use this function for customized exception handling.
+ * customized method call this method for all unknown case
+ * @param code exception code
+ * @param msg exception message
+ */
+ public final void readException(int code, String msg) {
+ switch (code) {
+ case EX_SECURITY:
+ throw new SecurityException(msg);
+ case EX_BAD_PARCELABLE:
+ throw new BadParcelableException(msg);
+ case EX_ILLEGAL_ARGUMENT:
+ throw new IllegalArgumentException(msg);
+ case EX_NULL_POINTER:
+ throw new NullPointerException(msg);
+ case EX_ILLEGAL_STATE:
+ throw new IllegalStateException(msg);
+ }
+ throw new RuntimeException("Unknown exception code: " + code
+ + " msg " + msg);
+ }
+
+ /**
+ * Read an integer value from the parcel at the current dataPosition().
+ */
+ public final native int readInt();
+
+ /**
+ * Read a long integer value from the parcel at the current dataPosition().
+ */
+ public final native long readLong();
+
+ /**
+ * Read a floating point value from the parcel at the current
+ * dataPosition().
+ */
+ public final native float readFloat();
+
+ /**
+ * Read a double precision floating point value from the parcel at the
+ * current dataPosition().
+ */
+ public final native double readDouble();
+
+ /**
+ * Read a string value from the parcel at the current dataPosition().
+ */
+ public final native String readString();
+
+ /**
+ * Read an object from the parcel at the current dataPosition().
+ */
+ public final native IBinder readStrongBinder();
+
+ /**
+ * Read a FileDescriptor from the parcel at the current dataPosition().
+ */
+ public final ParcelFileDescriptor readFileDescriptor() {
+ FileDescriptor fd = internalReadFileDescriptor();
+ return fd != null ? new ParcelFileDescriptor(fd) : null;
+ }
+
+ private native FileDescriptor internalReadFileDescriptor();
+ /*package*/ static native FileDescriptor openFileDescriptor(String file,
+ int mode) throws FileNotFoundException;
+ /*package*/ static native void closeFileDescriptor(FileDescriptor desc)
+ throws IOException;
+
+ /**
+ * Read a byte value from the parcel at the current dataPosition().
+ */
+ public final byte readByte() {
+ return (byte)(readInt() & 0xff);
+ }
+
+ /**
+ * Please use {@link #readBundle(ClassLoader)} instead (whose data must have
+ * been written with {@link #writeBundle}. Read into an existing Map object
+ * from the parcel at the current dataPosition().
+ */
+ public final void readMap(Map outVal, ClassLoader loader) {
+ int N = readInt();
+ readMapInternal(outVal, N, loader);
+ }
+
+ /**
+ * Read into an existing List object from the parcel at the current
+ * dataPosition(), using the given class loader to load any enclosed
+ * Parcelables. If it is null, the default class loader is used.
+ */
+ public final void readList(List outVal, ClassLoader loader) {
+ int N = readInt();
+ readListInternal(outVal, N, loader);
+ }
+
+ /**
+ * Please use {@link #readBundle(ClassLoader)} instead (whose data must have
+ * been written with {@link #writeBundle}. Read and return a new HashMap
+ * object from the parcel at the current dataPosition(), using the given
+ * class loader to load any enclosed Parcelables. Returns null if
+ * the previously written map object was null.
+ */
+ public final HashMap readHashMap(ClassLoader loader)
+ {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ HashMap m = new HashMap(N);
+ readMapInternal(m, N, loader);
+ return m;
+ }
+
+ /**
+ * Read and return a new Bundle object from the parcel at the current
+ * dataPosition(). Returns null if the previously written Bundle object was
+ * null.
+ */
+ public final Bundle readBundle() {
+ return readBundle(null);
+ }
+
+ /**
+ * Read and return a new Bundle object from the parcel at the current
+ * dataPosition(), using the given class loader to initialize the class
+ * loader of the Bundle for later retrieval of Parcelable objects.
+ * Returns null if the previously written Bundle object was null.
+ */
+ public final Bundle readBundle(ClassLoader loader) {
+ int offset = dataPosition();
+ int length = readInt();
+ if (length < 0) {
+ return null;
+ }
+ int magic = readInt();
+ if (magic != 0x4C444E42) {
+ //noinspection ThrowableInstanceNeverThrown
+ String st = Log.getStackTraceString(new RuntimeException());
+ Log.e("Bundle", "readBundle: bad magic number");
+ Log.e("Bundle", "readBundle: trace = " + st);
+ }
+
+ // Advance within this Parcel
+ setDataPosition(offset + length + 4);
+
+ Parcel p = new Parcel(0);
+ p.setDataPosition(0);
+ p.appendFrom(this, offset, length + 4);
+ p.setDataPosition(0);
+ final Bundle bundle = new Bundle(p);
+ if (loader != null) {
+ bundle.setClassLoader(loader);
+ }
+ return bundle;
+ }
+
+ /**
+ * Read and return a new Bundle object from the parcel at the current
+ * dataPosition(). Returns null if the previously written Bundle object was
+ * null. The returned bundle will have its contents fully unpacked using
+ * the given ClassLoader.
+ */
+ /* package */ Bundle readBundleUnpacked(ClassLoader loader) {
+ int length = readInt();
+ if (length == -1) {
+ return null;
+ }
+ int magic = readInt();
+ if (magic != 0x4C444E42) {
+ //noinspection ThrowableInstanceNeverThrown
+ String st = Log.getStackTraceString(new RuntimeException());
+ Log.e("Bundle", "readBundleUnpacked: bad magic number");
+ Log.e("Bundle", "readBundleUnpacked: trace = " + st);
+ }
+ Bundle m = new Bundle(loader);
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ readMapInternal(m.mMap, N, loader);
+ return m;
+ }
+
+ /**
+ * Read and return a byte[] object from the parcel.
+ */
+ public final native byte[] createByteArray();
+
+ /**
+ * Read a byte[] object from the parcel and copy it into the
+ * given byte array.
+ */
+ public final void readByteArray(byte[] val) {
+ // TODO: make this a native method to avoid the extra copy.
+ byte[] ba = createByteArray();
+ if (ba.length == val.length) {
+ System.arraycopy(ba, 0, val, 0, ba.length);
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+ /**
+ * Read and return a String[] object from the parcel.
+ * {@hide}
+ */
+ public final String[] readStringArray() {
+ String[] array = null;
+
+ int length = readInt();
+ if (length >= 0)
+ {
+ array = new String[length];
+
+ for (int i = 0 ; i < length ; i++)
+ {
+ array[i] = readString();
+ }
+ }
+
+ return array;
+ }
+
+ /**
+ * Read and return a new ArrayList object from the parcel at the current
+ * dataPosition(). Returns null if the previously written list object was
+ * null. The given class loader will be used to load any enclosed
+ * Parcelables.
+ */
+ public final ArrayList readArrayList(ClassLoader loader) {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ ArrayList l = new ArrayList(N);
+ readListInternal(l, N, loader);
+ return l;
+ }
+
+ /**
+ * Read and return a new Object array from the parcel at the current
+ * dataPosition(). Returns null if the previously written array was
+ * null. The given class loader will be used to load any enclosed
+ * Parcelables.
+ */
+ public final Object[] readArray(ClassLoader loader) {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ Object[] l = new Object[N];
+ readArrayInternal(l, N, loader);
+ return l;
+ }
+
+ /**
+ * Read and return a new SparseArray object from the parcel at the current
+ * dataPosition(). Returns null if the previously written list object was
+ * null. The given class loader will be used to load any enclosed
+ * Parcelables.
+ */
+ public final SparseArray readSparseArray(ClassLoader loader) {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ SparseArray sa = new SparseArray(N);
+ readSparseArrayInternal(sa, N, loader);
+ return sa;
+ }
+
+ /**
+ * Read and return a new SparseBooleanArray object from the parcel at the current
+ * dataPosition(). Returns null if the previously written list object was
+ * null.
+ */
+ public final SparseBooleanArray readSparseBooleanArray() {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ SparseBooleanArray sa = new SparseBooleanArray(N);
+ readSparseBooleanArrayInternal(sa, N);
+ return sa;
+ }
+
+ /**
+ * Read and return a new ArrayList containing a particular object type from
+ * the parcel that was written with {@link #writeTypedList} at the
+ * current dataPosition(). Returns null if the
+ * previously written list object was null. The list <em>must</em> have
+ * previously been written via {@link #writeTypedList} with the same object
+ * type.
+ *
+ * @return A newly created ArrayList containing objects with the same data
+ * as those that were previously written.
+ *
+ * @see #writeTypedList
+ */
+ public final <T> ArrayList<T> createTypedArrayList(Parcelable.Creator<T> c) {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ ArrayList<T> l = new ArrayList<T>(N);
+ while (N > 0) {
+ if (readInt() != 0) {
+ l.add(c.createFromParcel(this));
+ } else {
+ l.add(null);
+ }
+ N--;
+ }
+ return l;
+ }
+
+ /**
+ * Read into the given List items containing a particular object type
+ * that were written with {@link #writeTypedList} at the
+ * current dataPosition(). The list <em>must</em> have
+ * previously been written via {@link #writeTypedList} with the same object
+ * type.
+ *
+ * @return A newly created ArrayList containing objects with the same data
+ * as those that were previously written.
+ *
+ * @see #writeTypedList
+ */
+ public final <T> void readTypedList(List<T> list, Parcelable.Creator<T> c) {
+ int M = list.size();
+ int N = readInt();
+ int i = 0;
+ for (; i < M && i < N; i++) {
+ if (readInt() != 0) {
+ list.set(i, c.createFromParcel(this));
+ } else {
+ list.set(i, null);
+ }
+ }
+ for (; i<N; i++) {
+ if (readInt() != 0) {
+ list.add(c.createFromParcel(this));
+ } else {
+ list.add(null);
+ }
+ }
+ for (; i<M; i++) {
+ list.remove(N);
+ }
+ }
+
+ /**
+ * Read and return a new ArrayList containing String objects from
+ * the parcel that was written with {@link #writeStringList} at the
+ * current dataPosition(). Returns null if the
+ * previously written list object was null.
+ *
+ * @return A newly created ArrayList containing strings with the same data
+ * as those that were previously written.
+ *
+ * @see #writeStringList
+ */
+ public final ArrayList<String> createStringArrayList() {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ ArrayList<String> l = new ArrayList<String>(N);
+ while (N > 0) {
+ l.add(readString());
+ N--;
+ }
+ return l;
+ }
+
+ /**
+ * Read and return a new ArrayList containing IBinder objects from
+ * the parcel that was written with {@link #writeBinderList} at the
+ * current dataPosition(). Returns null if the
+ * previously written list object was null.
+ *
+ * @return A newly created ArrayList containing strings with the same data
+ * as those that were previously written.
+ *
+ * @see #writeBinderList
+ */
+ public final ArrayList<IBinder> createBinderArrayList() {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ ArrayList<IBinder> l = new ArrayList<IBinder>(N);
+ while (N > 0) {
+ l.add(readStrongBinder());
+ N--;
+ }
+ return l;
+ }
+
+ /**
+ * Read into the given List items String objects that were written with
+ * {@link #writeStringList} at the current dataPosition().
+ *
+ * @return A newly created ArrayList containing strings with the same data
+ * as those that were previously written.
+ *
+ * @see #writeStringList
+ */
+ public final void readStringList(List<String> list) {
+ int M = list.size();
+ int N = readInt();
+ int i = 0;
+ for (; i < M && i < N; i++) {
+ list.set(i, readString());
+ }
+ for (; i<N; i++) {
+ list.add(readString());
+ }
+ for (; i<M; i++) {
+ list.remove(N);
+ }
+ }
+
+ /**
+ * Read into the given List items IBinder objects that were written with
+ * {@link #writeBinderList} at the current dataPosition().
+ *
+ * @return A newly created ArrayList containing strings with the same data
+ * as those that were previously written.
+ *
+ * @see #writeBinderList
+ */
+ public final void readBinderList(List<IBinder> list) {
+ int M = list.size();
+ int N = readInt();
+ int i = 0;
+ for (; i < M && i < N; i++) {
+ list.set(i, readStrongBinder());
+ }
+ for (; i<N; i++) {
+ list.add(readStrongBinder());
+ }
+ for (; i<M; i++) {
+ list.remove(N);
+ }
+ }
+
+ /**
+ * Read and return a new array containing a particular object type from
+ * the parcel at the current dataPosition(). Returns null if the
+ * previously written array was null. The array <em>must</em> have
+ * previously been written via {@link #writeTypedArray} with the same
+ * object type.
+ *
+ * @return A newly created array containing objects with the same data
+ * as those that were previously written.
+ *
+ * @see #writeTypedArray
+ */
+ public final <T> T[] createTypedArray(Parcelable.Creator<T> c) {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ T[] l = c.newArray(N);
+ for (int i=0; i<N; i++) {
+ if (readInt() != 0) {
+ l[i] = c.createFromParcel(this);
+ }
+ }
+ return l;
+ }
+
+ public final <T> void readTypedArray(T[] val, Parcelable.Creator<T> c) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ if (readInt() != 0) {
+ val[i] = c.createFromParcel(this);
+ } else {
+ val[i] = null;
+ }
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+ /**
+ * @deprecated
+ * @hide
+ */
+ @Deprecated
+ public final <T> T[] readTypedArray(Parcelable.Creator<T> c) {
+ return createTypedArray(c);
+ }
+
+ /**
+ * Write a heterogeneous array of Parcelable objects into the Parcel.
+ * Each object in the array is written along with its class name, so
+ * that the correct class can later be instantiated. As a result, this
+ * has significantly more overhead than {@link #writeTypedArray}, but will
+ * correctly handle an array containing more than one type of object.
+ *
+ * @param value The array of objects to be written.
+ * @param parcelableFlags Contextual flags as per
+ * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+ *
+ * @see #writeTypedArray
+ */
+ public final <T extends Parcelable> void writeParcelableArray(T[] value,
+ int parcelableFlags) {
+ if (value != null) {
+ int N = value.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeParcelable(value[i], parcelableFlags);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ /**
+ * Read a typed object from a parcel. The given class loader will be
+ * used to load any enclosed Parcelables. If it is null, the default class
+ * loader will be used.
+ */
+ public final Object readValue(ClassLoader loader) {
+ int type = readInt();
+
+ switch (type) {
+ case VAL_NULL:
+ return null;
+
+ case VAL_STRING:
+ return readString();
+
+ case VAL_INTEGER:
+ return readInt();
+
+ case VAL_MAP:
+ return readHashMap(loader);
+
+ case VAL_PARCELABLE:
+ return readParcelable(loader);
+
+ case VAL_SHORT:
+ return (short) readInt();
+
+ case VAL_LONG:
+ return readLong();
+
+ case VAL_FLOAT:
+ return readFloat();
+
+ case VAL_DOUBLE:
+ return readDouble();
+
+ case VAL_BOOLEAN:
+ return readInt() == 1;
+
+ case VAL_CHARSEQUENCE:
+ return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(this);
+
+ case VAL_LIST:
+ return readArrayList(loader);
+
+ case VAL_BOOLEANARRAY:
+ return createBooleanArray();
+
+ case VAL_BYTEARRAY:
+ return createByteArray();
+
+ case VAL_STRINGARRAY:
+ return readStringArray();
+
+ case VAL_IBINDER:
+ return readStrongBinder();
+
+ case VAL_OBJECTARRAY:
+ return readArray(loader);
+
+ case VAL_INTARRAY:
+ return createIntArray();
+
+ case VAL_LONGARRAY:
+ return createLongArray();
+
+ case VAL_BYTE:
+ return readByte();
+
+ case VAL_SERIALIZABLE:
+ return readSerializable();
+
+ case VAL_PARCELABLEARRAY:
+ return readParcelableArray(loader);
+
+ case VAL_SPARSEARRAY:
+ return readSparseArray(loader);
+
+ case VAL_SPARSEBOOLEANARRAY:
+ return readSparseBooleanArray();
+
+ case VAL_BUNDLE:
+ return readBundle(loader); // loading will be deferred
+
+ default:
+ int off = dataPosition() - 4;
+ throw new RuntimeException(
+ "Parcel " + this + ": Unmarshalling unknown type code " + type + " at offset " + off);
+ }
+ }
+
+ /**
+ * Read and return a new Parcelable from the parcel. The given class loader
+ * will be used to load any enclosed Parcelables. If it is null, the default
+ * class loader will be used.
+ * @param loader A ClassLoader from which to instantiate the Parcelable
+ * object, or null for the default class loader.
+ * @return Returns the newly created Parcelable, or null if a null
+ * object has been written.
+ * @throws BadParcelableException Throws BadParcelableException if there
+ * was an error trying to instantiate the Parcelable.
+ */
+ public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
+ String name = readString();
+ if (name == null) {
+ return null;
+ }
+ Parcelable.Creator<T> creator;
+ synchronized (mCreators) {
+ HashMap<String,Parcelable.Creator> map = mCreators.get(loader);
+ if (map == null) {
+ map = new HashMap<String,Parcelable.Creator>();
+ mCreators.put(loader, map);
+ }
+ creator = map.get(name);
+ if (creator == null) {
+ try {
+ Class c = loader == null ?
+ Class.forName(name) : Class.forName(name, true, loader);
+ Field f = c.getField("CREATOR");
+ creator = (Parcelable.Creator)f.get(null);
+ }
+ catch (IllegalAccessException e) {
+ Log.e("Parcel", "Class not found when unmarshalling: "
+ + name + ", e: " + e);
+ throw new BadParcelableException(
+ "IllegalAccessException when unmarshalling: " + name);
+ }
+ catch (ClassNotFoundException e) {
+ Log.e("Parcel", "Class not found when unmarshalling: "
+ + name + ", e: " + e);
+ throw new BadParcelableException(
+ "ClassNotFoundException when unmarshalling: " + name);
+ }
+ catch (ClassCastException e) {
+ throw new BadParcelableException("Parcelable protocol requires a "
+ + "Parcelable.Creator object called "
+ + " CREATOR on class " + name);
+ }
+ catch (NoSuchFieldException e) {
+ throw new BadParcelableException("Parcelable protocol requires a "
+ + "Parcelable.Creator object called "
+ + " CREATOR on class " + name);
+ }
+ if (creator == null) {
+ throw new BadParcelableException("Parcelable protocol requires a "
+ + "Parcelable.Creator object called "
+ + " CREATOR on class " + name);
+ }
+
+ map.put(name, creator);
+ }
+ }
+
+ return creator.createFromParcel(this);
+ }
+
+ /**
+ * Read and return a new Parcelable array from the parcel.
+ * The given class loader will be used to load any enclosed
+ * Parcelables.
+ * @return the Parcelable array, or null if the array is null
+ */
+ public final Parcelable[] readParcelableArray(ClassLoader loader) {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ Parcelable[] p = new Parcelable[N];
+ for (int i = 0; i < N; i++) {
+ p[i] = (Parcelable) readParcelable(loader);
+ }
+ return p;
+ }
+
+ /**
+ * Read and return a new Serializable object from the parcel.
+ * @return the Serializable object, or null if the Serializable name
+ * wasn't found in the parcel.
+ */
+ public final Serializable readSerializable() {
+ String name = readString();
+ if (name == null) {
+ // For some reason we were unable to read the name of the Serializable (either there
+ // is nothing left in the Parcel to read, or the next value wasn't a String), so
+ // return null, which indicates that the name wasn't found in the parcel.
+ return null;
+ }
+
+ byte[] serializedData = createByteArray();
+ ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
+ try {
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ return (Serializable) ois.readObject();
+ } catch (IOException ioe) {
+ throw new RuntimeException("Parcelable encountered " +
+ "IOException reading a Serializable object (name = " + name +
+ ")", ioe);
+ } catch (ClassNotFoundException cnfe) {
+ throw new RuntimeException("Parcelable encountered" +
+ "ClassNotFoundException reading a Serializable object (name = "
+ + name + ")", cnfe);
+ }
+ }
+
+ // Cache of previously looked up CREATOR.createFromParcel() methods for
+ // particular classes. Keys are the names of the classes, values are
+ // Method objects.
+ private static final HashMap<ClassLoader,HashMap<String,Parcelable.Creator>>
+ mCreators = new HashMap<ClassLoader,HashMap<String,Parcelable.Creator>>();
+
+ static protected final Parcel obtain(int obj) {
+ final Parcel[] pool = sHolderPool;
+ synchronized (pool) {
+ Parcel p;
+ for (int i=0; i<POOL_SIZE; i++) {
+ p = pool[i];
+ if (p != null) {
+ pool[i] = null;
+ if (DEBUG_RECYCLE) {
+ p.mStack = new RuntimeException();
+ }
+ p.init(obj);
+ return p;
+ }
+ }
+ }
+ return new Parcel(obj);
+ }
+
+ private Parcel(int obj) {
+ if (DEBUG_RECYCLE) {
+ mStack = new RuntimeException();
+ }
+ //Log.i("Parcel", "Initializing obj=0x" + Integer.toHexString(obj), mStack);
+ init(obj);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (DEBUG_RECYCLE) {
+ if (mStack != null) {
+ Log.w("Parcel", "Client did not call Parcel.recycle()", mStack);
+ }
+ }
+ destroy();
+ }
+
+ private native void freeBuffer();
+ private native void init(int obj);
+ private native void destroy();
+
+ private void readMapInternal(Map outVal, int N,
+ ClassLoader loader) {
+ while (N > 0) {
+ Object key = readValue(loader);
+ Object value = readValue(loader);
+ outVal.put(key, value);
+ N--;
+ }
+ }
+
+ private void readListInternal(List outVal, int N,
+ ClassLoader loader) {
+ while (N > 0) {
+ Object value = readValue(loader);
+ //Log.d("Parcel", "Unmarshalling value=" + value);
+ outVal.add(value);
+ N--;
+ }
+ }
+
+ private void readArrayInternal(Object[] outVal, int N,
+ ClassLoader loader) {
+ for (int i = 0; i < N; i++) {
+ Object value = readValue(loader);
+ //Log.d("Parcel", "Unmarshalling value=" + value);
+ outVal[i] = value;
+ }
+ }
+
+ private void readSparseArrayInternal(SparseArray outVal, int N,
+ ClassLoader loader) {
+ while (N > 0) {
+ int key = readInt();
+ Object value = readValue(loader);
+ //Log.i("Parcel", "Unmarshalling key=" + key + " value=" + value);
+ outVal.append(key, value);
+ N--;
+ }
+ }
+
+
+ private void readSparseBooleanArrayInternal(SparseBooleanArray outVal, int N) {
+ while (N > 0) {
+ int key = readInt();
+ boolean value = this.readByte() == 1;
+ //Log.i("Parcel", "Unmarshalling key=" + key + " value=" + value);
+ outVal.append(key, value);
+ N--;
+ }
+ }
+}
diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl
new file mode 100644
index 0000000..5857aae
--- /dev/null
+++ b/core/java/android/os/ParcelFileDescriptor.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/os/ParcelFileDescriptor.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable ParcelFileDescriptor;
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
new file mode 100644
index 0000000..3fcb18e
--- /dev/null
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.Socket;
+
+/**
+ * The FileDescriptor returned by {@link Parcel#readFileDescriptor}, allowing
+ * you to close it when done with it.
+ */
+public class ParcelFileDescriptor implements Parcelable {
+ private final FileDescriptor mFileDescriptor;
+ private boolean mClosed;
+ //this field is to create wrapper for ParcelFileDescriptor using another
+ //PartialFileDescriptor but avoid invoking close twice
+ //consider ParcelFileDescriptor A(fileDescriptor fd), ParcelFileDescriptor B(A)
+ //in this particular case fd.close might be invoked twice.
+ private final ParcelFileDescriptor mParcelDescriptor;
+
+ /**
+ * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied
+ * and this file doesn't already exist, then create the file with
+ * permissions such that any application can read it.
+ */
+ public static final int MODE_WORLD_READABLE = 0x00000001;
+
+ /**
+ * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied
+ * and this file doesn't already exist, then create the file with
+ * permissions such that any application can write it.
+ */
+ public static final int MODE_WORLD_WRITEABLE = 0x00000002;
+
+ /**
+ * For use with {@link #open}: open the file with read-only access.
+ */
+ public static final int MODE_READ_ONLY = 0x10000000;
+
+ /**
+ * For use with {@link #open}: open the file with write-only access.
+ */
+ public static final int MODE_WRITE_ONLY = 0x20000000;
+
+ /**
+ * For use with {@link #open}: open the file with read and write access.
+ */
+ public static final int MODE_READ_WRITE = 0x30000000;
+
+ /**
+ * For use with {@link #open}: create the file if it doesn't already exist.
+ */
+ public static final int MODE_CREATE = 0x08000000;
+
+ /**
+ * For use with {@link #open}: erase contents of file when opening.
+ */
+ public static final int MODE_TRUNCATE = 0x04000000;
+
+ /**
+ * For use with {@link #open}: append to end of file while writing.
+ */
+ public static final int MODE_APPEND = 0x02000000;
+
+ /**
+ * Create a new ParcelFileDescriptor accessing a given file.
+ *
+ * @param file The file to be opened.
+ * @param mode The desired access mode, must be one of
+ * {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or
+ * {@link #MODE_READ_WRITE}; may also be any combination of
+ * {@link #MODE_CREATE}, {@link #MODE_TRUNCATE},
+ * {@link #MODE_WORLD_READABLE}, and {@link #MODE_WORLD_WRITEABLE}.
+ *
+ * @return Returns a new ParcelFileDescriptor pointing to the given
+ * file.
+ *
+ * @throws FileNotFoundException Throws FileNotFoundException if the given
+ * file does not exist or can not be opened with the requested mode.
+ */
+ public static ParcelFileDescriptor open(File file, int mode)
+ throws FileNotFoundException {
+ String path = file.getPath();
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkRead(path);
+ if ((mode&MODE_WRITE_ONLY) != 0) {
+ security.checkWrite(path);
+ }
+ }
+
+ if ((mode&MODE_READ_WRITE) == 0) {
+ throw new IllegalArgumentException(
+ "Must specify MODE_READ_ONLY, MODE_WRITE_ONLY, or MODE_READ_WRITE");
+ }
+
+ FileDescriptor fd = Parcel.openFileDescriptor(path, mode);
+ return new ParcelFileDescriptor(fd);
+ }
+
+ /**
+ * Create a new ParcelFileDescriptor from the specified Socket.
+ *
+ * @param socket The Socket whose FileDescriptor is used to create
+ * a new ParcelFileDescriptor.
+ *
+ * @return A new ParcelFileDescriptor with the FileDescriptor of the
+ * specified Socket.
+ */
+ public static ParcelFileDescriptor fromSocket(Socket socket) {
+ FileDescriptor fd = getFileDescriptorFromSocket(socket);
+ return new ParcelFileDescriptor(fd);
+ }
+
+ // Extracts the file descriptor from the specified socket and returns it untouched
+ private static native FileDescriptor getFileDescriptorFromSocket(Socket socket);
+
+ /**
+ * Retrieve the actual FileDescriptor associated with this object.
+ *
+ * @return Returns the FileDescriptor associated with this object.
+ */
+ public FileDescriptor getFileDescriptor() {
+ return mFileDescriptor;
+ }
+
+ /**
+ * Return the total size of the file representing this fd, as determined
+ * by stat(). Returns -1 if the fd is not a file.
+ */
+ public native long getStatSize();
+
+ /**
+ * This is needed for implementing AssetFileDescriptor.AutoCloseOutputStream,
+ * and I really don't think we want it to be public.
+ * @hide
+ */
+ public native long seekTo(long pos);
+
+ /**
+ * Close the ParcelFileDescriptor. This implementation closes the underlying
+ * OS resources allocated to represent this stream.
+ *
+ * @throws IOException
+ * If an error occurs attempting to close this ParcelFileDescriptor.
+ */
+ public void close() throws IOException {
+ mClosed = true;
+ if (mParcelDescriptor != null) {
+ // If this is a proxy to another file descriptor, just call through to its
+ // close method.
+ mParcelDescriptor.close();
+ } else {
+ Parcel.closeFileDescriptor(mFileDescriptor);
+ }
+ }
+
+ /**
+ * An InputStream you can create on a ParcelFileDescriptor, which will
+ * take care of calling {@link ParcelFileDescriptor#close
+ * ParcelFileDescritor.close()} for you when the stream is closed.
+ */
+ public static class AutoCloseInputStream extends FileInputStream {
+ private final ParcelFileDescriptor mFd;
+
+ public AutoCloseInputStream(ParcelFileDescriptor fd) {
+ super(fd.getFileDescriptor());
+ mFd = fd;
+ }
+
+ @Override
+ public void close() throws IOException {
+ mFd.close();
+ }
+ }
+
+ /**
+ * An OutputStream you can create on a ParcelFileDescriptor, which will
+ * take care of calling {@link ParcelFileDescriptor#close
+ * ParcelFileDescritor.close()} for you when the stream is closed.
+ */
+ public static class AutoCloseOutputStream extends FileOutputStream {
+ private final ParcelFileDescriptor mFd;
+
+ public AutoCloseOutputStream(ParcelFileDescriptor fd) {
+ super(fd.getFileDescriptor());
+ mFd = fd;
+ }
+
+ @Override
+ public void close() throws IOException {
+ mFd.close();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{ParcelFileDescriptor: " + mFileDescriptor + "}";
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (!mClosed) {
+ close();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ public ParcelFileDescriptor(ParcelFileDescriptor descriptor) {
+ super();
+ mParcelDescriptor = descriptor;
+ mFileDescriptor = mParcelDescriptor.mFileDescriptor;
+ }
+
+ /*package */ParcelFileDescriptor(FileDescriptor descriptor) {
+ super();
+ mFileDescriptor = descriptor;
+ mParcelDescriptor = null;
+ }
+
+ /* Parcelable interface */
+ public int describeContents() {
+ return Parcelable.CONTENTS_FILE_DESCRIPTOR;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeFileDescriptor(mFileDescriptor);
+ if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
+ try {
+ close();
+ } catch (IOException e) {
+ // Empty
+ }
+ }
+ }
+
+ public static final Parcelable.Creator<ParcelFileDescriptor> CREATOR
+ = new Parcelable.Creator<ParcelFileDescriptor>() {
+ public ParcelFileDescriptor createFromParcel(Parcel in) {
+ return in.readFileDescriptor();
+ }
+ public ParcelFileDescriptor[] newArray(int size) {
+ return new ParcelFileDescriptor[size];
+ }
+ };
+
+}
diff --git a/core/java/android/os/ParcelFormatException.java b/core/java/android/os/ParcelFormatException.java
new file mode 100644
index 0000000..8b6fda0
--- /dev/null
+++ b/core/java/android/os/ParcelFormatException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * The contents of a Parcel (usually during unmarshalling) does not
+ * contain the expected data.
+ */
+public class ParcelFormatException extends RuntimeException {
+ public ParcelFormatException() {
+ super();
+ }
+
+ public ParcelFormatException(String reason) {
+ super(reason);
+ }
+}
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
new file mode 100644
index 0000000..aee1e0b
--- /dev/null
+++ b/core/java/android/os/Parcelable.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * Interface for classes whose instances can be written to
+ * and restored from a {@link Parcel}. Classes implementing the Parcelable
+ * interface must also have a static field called <code>CREATOR</code>, which
+ * is an object implementing the {@link Parcelable.Creator Parcelable.Creator}
+ * interface.
+ *
+ * <p>A typical implementation of Parcelable is:</p>
+ *
+ * <pre>
+ * public class MyParcelable implements Parcelable {
+ * private int mData;
+ *
+ * public void writeToParcel(Parcel out, int flags) {
+ * out.writeInt(mData);
+ * }
+ *
+ * public static final Parcelable.Creator<MyParcelable> CREATOR
+ * = new Parcelable.Creator<MyParcelable>() {
+ * public MyParcelable createFromParcel(Parcel in) {
+ * return new MyParcelable(in);
+ * }
+ *
+ * public MyParcelable[] newArray(int size) {
+ * return new MyParcelable[size];
+ * }
+ * }
+ *
+ * private MyParcelable(Parcel in) {
+ * mData = in.readInt();
+ * }
+ * }</pre>
+ */
+public interface Parcelable {
+ /**
+ * Flag for use with {@link #writeToParcel}: the object being written
+ * is a return value, that is the result of a function such as
+ * "<code>Parcelable someFunction()</code>",
+ * "<code>void someFunction(out Parcelable)</code>", or
+ * "<code>void someFunction(inout Parcelable)</code>". Some implementations
+ * may want to release resources at this point.
+ */
+ public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
+
+ /**
+ * Bit masks for use with {@link #describeContents}: each bit represents a
+ * kind of object considered to have potential special significance when
+ * marshalled.
+ */
+ public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
+
+ /**
+ * Describe the kinds of special objects contained in this Parcelable's
+ * marshalled representation.
+ *
+ * @return a bitmask indicating the set of special object types marshalled
+ * by the Parcelable.
+ */
+ public int describeContents();
+
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ public void writeToParcel(Parcel dest, int flags);
+
+ /**
+ * Interface that must be implemented and provided as a public CREATOR
+ * field that generates instances of your Parcelable class from a Parcel.
+ */
+ public interface Creator<T> {
+ /**
+ * Create a new instance of the Parcelable class, instantiating it
+ * from the given Parcel whose data had previously been written by
+ * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
+ *
+ * @param source The Parcel to read the object's data from.
+ * @return Returns a new instance of the Parcelable class.
+ */
+ public T createFromParcel(Parcel source);
+
+ /**
+ * Create a new array of the Parcelable class.
+ *
+ * @param size Size of the array.
+ * @return Returns an array of the Parcelable class, with every entry
+ * initialized to null.
+ */
+ public T[] newArray(int size);
+ }
+}
diff --git a/core/java/android/os/PatternMatcher.aidl b/core/java/android/os/PatternMatcher.aidl
new file mode 100644
index 0000000..86309f1
--- /dev/null
+++ b/core/java/android/os/PatternMatcher.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 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 android.os;
+
+parcelable PatternMatcher;
diff --git a/core/java/android/os/PatternMatcher.java b/core/java/android/os/PatternMatcher.java
new file mode 100644
index 0000000..56dc837
--- /dev/null
+++ b/core/java/android/os/PatternMatcher.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2008 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 android.os;
+
+/**
+ * A simple pattern matcher, which is safe to use on untrusted data: it does
+ * not provide full reg-exp support, only simple globbing that can not be
+ * used maliciously.
+ */
+public class PatternMatcher implements Parcelable {
+ /**
+ * Pattern type: the given pattern must exactly match the string it is
+ * tested against.
+ */
+ public static final int PATTERN_LITERAL = 0;
+
+ /**
+ * Pattern type: the given pattern must match the
+ * beginning of the string it is tested against.
+ */
+ public static final int PATTERN_PREFIX = 1;
+
+ /**
+ * Pattern type: the given pattern is interpreted with a
+ * simple glob syntax for matching against the string it is tested against.
+ * In this syntax, you can use the '*' character to match against zero or
+ * more occurrences of the character immediately before. If the
+ * character before it is '.' it will match any character. The character
+ * '\' can be used as an escape. This essentially provides only the '*'
+ * wildcard part of a normal regexp.
+ */
+ public static final int PATTERN_SIMPLE_GLOB = 2;
+
+ private final String mPattern;
+ private final int mType;
+
+ public PatternMatcher(String pattern, int type) {
+ mPattern = pattern;
+ mType = type;
+ }
+
+ public final String getPath() {
+ return mPattern;
+ }
+
+ public final int getType() {
+ return mType;
+ }
+
+ public boolean match(String str) {
+ return matchPattern(mPattern, str, mType);
+ }
+
+ public String toString() {
+ String type = "? ";
+ switch (mType) {
+ case PATTERN_LITERAL:
+ type = "LITERAL: ";
+ break;
+ case PATTERN_PREFIX:
+ type = "PREFIX: ";
+ break;
+ case PATTERN_SIMPLE_GLOB:
+ type = "GLOB: ";
+ break;
+ }
+ return "PatternMatcher{" + type + mPattern + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mPattern);
+ dest.writeInt(mType);
+ }
+
+ public PatternMatcher(Parcel src) {
+ mPattern = src.readString();
+ mType = src.readInt();
+ }
+
+ public static final Parcelable.Creator<PatternMatcher> CREATOR
+ = new Parcelable.Creator<PatternMatcher>() {
+ public PatternMatcher createFromParcel(Parcel source) {
+ return new PatternMatcher(source);
+ }
+
+ public PatternMatcher[] newArray(int size) {
+ return new PatternMatcher[size];
+ }
+ };
+
+ static boolean matchPattern(String pattern, String match, int type) {
+ if (match == null) return false;
+ if (type == PATTERN_LITERAL) {
+ return pattern.equals(match);
+ } if (type == PATTERN_PREFIX) {
+ return match.startsWith(pattern);
+ } else if (type != PATTERN_SIMPLE_GLOB) {
+ return false;
+ }
+
+ final int NP = pattern.length();
+ if (NP <= 0) {
+ return match.length() <= 0;
+ }
+ final int NM = match.length();
+ int ip = 0, im = 0;
+ char nextChar = pattern.charAt(0);
+ while ((ip<NP) && (im<NM)) {
+ char c = nextChar;
+ ip++;
+ nextChar = ip < NP ? pattern.charAt(ip) : 0;
+ final boolean escaped = (c == '\\');
+ if (escaped) {
+ c = nextChar;
+ ip++;
+ nextChar = ip < NP ? pattern.charAt(ip) : 0;
+ }
+ if (nextChar == '*') {
+ if (!escaped && c == '.') {
+ if (ip >= (NP-1)) {
+ // at the end with a pattern match, so
+ // all is good without checking!
+ return true;
+ }
+ ip++;
+ nextChar = pattern.charAt(ip);
+ // Consume everything until the next character in the
+ // pattern is found.
+ if (nextChar == '\\') {
+ ip++;
+ nextChar = ip < NP ? pattern.charAt(ip) : 0;
+ }
+ do {
+ if (match.charAt(im) == nextChar) {
+ break;
+ }
+ im++;
+ } while (im < NM);
+ if (im == NM) {
+ // Whoops, the next character in the pattern didn't
+ // exist in the match.
+ return false;
+ }
+ ip++;
+ nextChar = ip < NP ? pattern.charAt(ip) : 0;
+ im++;
+ } else {
+ // Consume only characters matching the one before '*'.
+ do {
+ if (match.charAt(im) != c) {
+ break;
+ }
+ im++;
+ } while (im < NM);
+ ip++;
+ nextChar = ip < NP ? pattern.charAt(ip) : 0;
+ }
+ } else {
+ if (c != '.' && match.charAt(im) != c) return false;
+ im++;
+ }
+ }
+
+ if (ip >= NP && im >= NM) {
+ // Reached the end of both strings, all is good!
+ return true;
+ }
+
+ // One last check: we may have finished the match string, but still
+ // have a '.*' at the end of the pattern, which should still count
+ // as a match.
+ if (ip == NP-2 && pattern.charAt(ip) == '.'
+ && pattern.charAt(ip+1) == '*') {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java
new file mode 100644
index 0000000..b53e227
--- /dev/null
+++ b/core/java/android/os/Power.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.io.IOException;
+
+/**
+ * Class that provides access to some of the power management functions.
+ *
+ * {@hide}
+ */
+public class Power
+{
+ // can't instantiate this class
+ private Power()
+ {
+ }
+
+ /**
+ * Wake lock that ensures that the CPU is running. The screen might
+ * not be on.
+ */
+ public static final int PARTIAL_WAKE_LOCK = 1;
+
+ /**
+ * Wake lock that ensures that the screen is on.
+ */
+ public static final int FULL_WAKE_LOCK = 2;
+
+ public static native void acquireWakeLock(int lock, String id);
+ public static native void releaseWakeLock(String id);
+
+ /**
+ * Flag to turn on and off the keyboard light.
+ */
+ public static final int KEYBOARD_LIGHT = 0x00000001;
+
+ /**
+ * Flag to turn on and off the screen backlight.
+ */
+ public static final int SCREEN_LIGHT = 0x00000002;
+
+ /**
+ * Flag to turn on and off the button backlight.
+ */
+ public static final int BUTTON_LIGHT = 0x00000004;
+
+ /**
+ * Flags to turn on and off all the backlights.
+ */
+ public static final int ALL_LIGHTS = (KEYBOARD_LIGHT|SCREEN_LIGHT|BUTTON_LIGHT);
+
+ /**
+ * Brightness value for fully off
+ */
+ public static final int BRIGHTNESS_OFF = 0;
+
+ /**
+ * Brightness value for dim backlight
+ */
+ public static final int BRIGHTNESS_DIM = 20;
+
+ /**
+ * Brightness value for fully on
+ */
+ public static final int BRIGHTNESS_ON = 255;
+
+ /**
+ * Brightness value to use when battery is low
+ */
+ public static final int BRIGHTNESS_LOW_BATTERY = 10;
+
+ /**
+ * Threshold for BRIGHTNESS_LOW_BATTERY (percentage)
+ * Screen will stay dim if battery level is <= LOW_BATTERY_THRESHOLD
+ */
+ public static final int LOW_BATTERY_THRESHOLD = 10;
+
+ /**
+ * Set the brightness for one or more lights
+ *
+ * @param mask flags indicating which lights to change brightness
+ * @param brightness new brightness value (0 = off, 255 = fully bright)
+ */
+ public static native int setLightBrightness(int mask, int brightness);
+
+ /**
+ * Turn the screen on or off
+ *
+ * @param on Whether you want the screen on or off
+ */
+ public static native int setScreenState(boolean on);
+
+ public static native int setLastUserActivityTimeout(long ms);
+
+ /**
+ * Turn the device off.
+ *
+ * This method is considered deprecated in favor of
+ * {@link android.policy.ShutdownThread.shutdownAfterDisablingRadio()}.
+ *
+ * @deprecated
+ * @hide
+ */
+ @Deprecated
+ public static native void shutdown();
+
+ /**
+ * Reboot the device.
+ * @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 native void reboot(String reason) throws IOException;
+}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
new file mode 100644
index 0000000..bfcf2fc
--- /dev/null
+++ b/core/java/android/os/PowerManager.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+import com.android.internal.os.RuntimeInit;
+
+/**
+ * This class gives you control of the power state of the device.
+ *
+ * <p><b>Device battery life will be significantly affected by the use of this API.</b> Do not
+ * acquire WakeLocks unless you really need them, use the minimum levels possible, and be sure
+ * to release it as soon as you can.
+ *
+ * <p>You can obtain an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
+ *
+ * <p>The primary API you'll use is {@link #newWakeLock(int, String) newWakeLock()}. This will
+ * create a {@link PowerManager.WakeLock} object. You can then use methods on this object to
+ * control the power state of the device. In practice it's quite simple:
+ *
+ * {@samplecode
+ * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ * PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
+ * wl.acquire();
+ * ..screen will stay on during this section..
+ * wl.release();
+ * }
+ *
+ * <p>The following flags are defined, with varying effects on system power. <i>These flags are
+ * mutually exclusive - you may only specify one of them.</i>
+ * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
+ *
+ * <thead>
+ * <tr><th>Flag Value</th>
+ * <th>CPU</th> <th>Screen</th> <th>Keyboard</th></tr>
+ * </thead>
+ *
+ * <tbody>
+ * <tr><th>{@link #PARTIAL_WAKE_LOCK}</th>
+ * <td>On*</td> <td>Off</td> <td>Off</td>
+ * </tr>
+ *
+ * <tr><th>{@link #SCREEN_DIM_WAKE_LOCK}</th>
+ * <td>On</td> <td>Dim</td> <td>Off</td>
+ * </tr>
+ *
+ * <tr><th>{@link #SCREEN_BRIGHT_WAKE_LOCK}</th>
+ * <td>On</td> <td>Bright</td> <td>Off</td>
+ * </tr>
+ *
+ * <tr><th>{@link #FULL_WAKE_LOCK}</th>
+ * <td>On</td> <td>Bright</td> <td>Bright</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <p>*<i>If you hold a partial wakelock, the CPU will continue to run, irrespective of any timers
+ * and even after the user presses the power button. In all other wakelocks, the CPU will run, but
+ * the user can still put the device to sleep using the power button.</i>
+ *
+ * <p>In addition, you can add two more flags, which affect behavior of the screen only. <i>These
+ * flags have no effect when combined with a {@link #PARTIAL_WAKE_LOCK}.</i>
+ * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
+ *
+ * <thead>
+ * <tr><th>Flag Value</th> <th>Description</th></tr>
+ * </thead>
+ *
+ * <tbody>
+ * <tr><th>{@link #ACQUIRE_CAUSES_WAKEUP}</th>
+ * <td>Normal wake locks don't actually turn on the illumination. Instead, they cause
+ * the illumination to remain on once it turns on (e.g. from user activity). This flag
+ * will force the screen and/or keyboard to turn on immediately, when the WakeLock is
+ * acquired. A typical use would be for notifications which are important for the user to
+ * see immediately.</td>
+ * </tr>
+ *
+ * <tr><th>{@link #ON_AFTER_RELEASE}</th>
+ * <td>If this flag is set, the user activity timer will be reset when the WakeLock is
+ * released, causing the illumination to remain on a bit longer. This can be used to
+ * reduce flicker if you are cycling between wake lock conditions.</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ *
+ */
+public class PowerManager
+{
+ private static final String TAG = "PowerManager";
+
+ /**
+ * These internal values define the underlying power elements that we might
+ * want to control individually. Eventually we'd like to expose them.
+ */
+ private static final int WAKE_BIT_CPU_STRONG = 1;
+ private static final int WAKE_BIT_CPU_WEAK = 2;
+ private static final int WAKE_BIT_SCREEN_DIM = 4;
+ private static final int WAKE_BIT_SCREEN_BRIGHT = 8;
+ private static final int WAKE_BIT_KEYBOARD_BRIGHT = 16;
+
+ private static final int LOCK_MASK = WAKE_BIT_CPU_STRONG
+ | WAKE_BIT_CPU_WEAK
+ | WAKE_BIT_SCREEN_DIM
+ | WAKE_BIT_SCREEN_BRIGHT
+ | WAKE_BIT_KEYBOARD_BRIGHT;
+
+ /**
+ * Wake lock that ensures that the CPU is running. The screen might
+ * not be on.
+ */
+ public static final int PARTIAL_WAKE_LOCK = WAKE_BIT_CPU_STRONG;
+
+ /**
+ * Wake lock that ensures that the screen and keyboard are on at
+ * full brightness.
+ */
+ public static final int FULL_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_BRIGHT
+ | WAKE_BIT_KEYBOARD_BRIGHT;
+
+ /**
+ * Wake lock that ensures that the screen is on at full brightness;
+ * the keyboard backlight will be allowed to go off.
+ */
+ public static final int SCREEN_BRIGHT_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_BRIGHT;
+
+ /**
+ * Wake lock that ensures that the screen is on (but may be dimmed);
+ * the keyboard backlight will be allowed to go off.
+ */
+ public static final int SCREEN_DIM_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_DIM;
+
+ /**
+ * Normally wake locks don't actually wake the device, they just cause
+ * it to remain on once it's already on. Think of the video player
+ * app as the normal behavior. Notifications that pop up and want
+ * the device to be on are the exception; use this flag to be like them.
+ * <p>
+ * Does not work with PARTIAL_WAKE_LOCKs.
+ */
+ public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000;
+
+ /**
+ * When this wake lock is released, poke the user activity timer
+ * so the screen stays on for a little longer.
+ * <p>
+ * Will not turn the screen on if it is not already on. See {@link #ACQUIRE_CAUSES_WAKEUP}
+ * if you want that.
+ * <p>
+ * Does not work with PARTIAL_WAKE_LOCKs.
+ */
+ public static final int ON_AFTER_RELEASE = 0x20000000;
+
+ /**
+ * Class lets you say that you need to have the device on.
+ *
+ * <p>Call release when you are done and don't need the lock anymore.
+ */
+ public class WakeLock
+ {
+ static final int RELEASE_WAKE_LOCK = 1;
+
+ Runnable mReleaser = new Runnable() {
+ public void run() {
+ release();
+ }
+ };
+
+ int mFlags;
+ String mTag;
+ IBinder mToken;
+ int mCount = 0;
+ boolean mRefCounted = true;
+ boolean mHeld = false;
+
+ WakeLock(int flags, String tag)
+ {
+ switch (flags & LOCK_MASK) {
+ case PARTIAL_WAKE_LOCK:
+ case SCREEN_DIM_WAKE_LOCK:
+ case SCREEN_BRIGHT_WAKE_LOCK:
+ case FULL_WAKE_LOCK:
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ mFlags = flags;
+ mTag = tag;
+ mToken = new Binder();
+ }
+
+ /**
+ * Sets whether this WakeLock is ref counted.
+ *
+ * @param value true for ref counted, false for not ref counted.
+ */
+ public void setReferenceCounted(boolean value)
+ {
+ mRefCounted = value;
+ }
+
+ /**
+ * Makes sure the device is on at the level you asked when you created
+ * the wake lock.
+ */
+ public void acquire()
+ {
+ synchronized (mToken) {
+ if (!mRefCounted || mCount++ == 0) {
+ try {
+ mService.acquireWakeLock(mFlags, mToken, mTag);
+ } catch (RemoteException e) {
+ }
+ mHeld = true;
+ }
+ }
+ }
+
+ /**
+ * Makes sure the device is on at the level you asked when you created
+ * the wake lock. The lock will be released after the given timeout.
+ *
+ * @param timeout Release the lock after the give timeout in milliseconds.
+ */
+ public void acquire(long timeout) {
+ acquire();
+ mHandler.postDelayed(mReleaser, timeout);
+ }
+
+
+ /**
+ * Release your claim to the CPU or screen being on.
+ *
+ * <p>
+ * It may turn off shortly after you release it, or it may not if there
+ * are other wake locks held.
+ */
+ public void release()
+ {
+ synchronized (mToken) {
+ if (!mRefCounted || --mCount == 0) {
+ try {
+ mService.releaseWakeLock(mToken);
+ } catch (RemoteException e) {
+ }
+ mHeld = false;
+ }
+ if (mCount < 0) {
+ throw new RuntimeException("WakeLock under-locked " + mTag);
+ }
+ }
+ }
+
+ public boolean isHeld()
+ {
+ synchronized (mToken) {
+ return mHeld;
+ }
+ }
+
+ public String toString() {
+ synchronized (mToken) {
+ return "WakeLock{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " held=" + mHeld + ", refCount=" + mCount + "}";
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable
+ {
+ synchronized (mToken) {
+ if (mHeld) {
+ try {
+ mService.releaseWakeLock(mToken);
+ } catch (RemoteException e) {
+ }
+ RuntimeInit.crash(TAG, new Exception(
+ "WakeLock finalized while still held: "+mTag));
+ }
+ }
+ }
+ }
+
+ /**
+ * Get a wake lock at the level of the flags parameter. Call
+ * {@link WakeLock#acquire() acquire()} on the object to acquire the
+ * wake lock, and {@link WakeLock#release release()} when you are done.
+ *
+ * {@samplecode
+ *PowerManager pm = (PowerManager)mContext.getSystemService(
+ * Context.POWER_SERVICE);
+ *PowerManager.WakeLock wl = pm.newWakeLock(
+ * PowerManager.SCREEN_DIM_WAKE_LOCK
+ * | PowerManager.ON_AFTER_RELEASE,
+ * TAG);
+ *wl.acquire();
+ * // ...
+ *wl.release();
+ * }
+ *
+ * @param flags Combination of flag values defining the requested behavior of the WakeLock.
+ * @param tag Your class name (or other tag) for debugging purposes.
+ *
+ * @see WakeLock#acquire()
+ * @see WakeLock#release()
+ */
+ public WakeLock newWakeLock(int flags, String tag)
+ {
+ return new WakeLock(flags, tag);
+ }
+
+ /**
+ * User activity happened.
+ * <p>
+ * Turns the device from whatever state it's in to full on, and resets
+ * the auto-off timer.
+ *
+ * @param when is used to order this correctly with the wake lock calls.
+ * This time should be in the {@link SystemClock#uptimeMillis
+ * SystemClock.uptimeMillis()} time base.
+ * @param noChangeLights should be true if you don't want the lights to
+ * turn on because of this event. This is set when the power
+ * key goes down. We want the device to stay on while the button
+ * is down, but we're about to turn off. Otherwise the lights
+ * flash on and then off and it looks weird.
+ */
+ public void userActivity(long when, boolean noChangeLights)
+ {
+ try {
+ mService.userActivity(when, noChangeLights);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Force the device to go to sleep. Overrides all the wake locks that are
+ * held.
+ *
+ * @param time is used to order this correctly with the wake lock calls.
+ * The time should be in the {@link SystemClock#uptimeMillis
+ * SystemClock.uptimeMillis()} time base.
+ */
+ public void goToSleep(long time)
+ {
+ try {
+ mService.goToSleep(time);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private PowerManager()
+ {
+ }
+
+ /**
+ * {@hide}
+ */
+ public PowerManager(IPowerManager service, Handler handler)
+ {
+ mService = service;
+ mHandler = handler;
+ }
+
+ /**
+ * TODO: It would be nice to be able to set the poke lock here,
+ * but I'm not sure what would be acceptable as an interface -
+ * either a PokeLock object (like WakeLock) or, possibly just a
+ * method call to set the poke lock.
+ */
+
+ IPowerManager mService;
+ Handler mHandler;
+}
+
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
new file mode 100644
index 0000000..cd86fbe
--- /dev/null
+++ b/core/java/android/os/Process.java
@@ -0,0 +1,706 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.net.LocalSocketAddress;
+import android.net.LocalSocket;
+import android.util.Log;
+import dalvik.system.Zygote;
+
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+
+/*package*/ class ZygoteStartFailedEx extends Exception {
+ /**
+ * Something prevented the zygote process startup from happening normally
+ */
+
+ ZygoteStartFailedEx() {};
+ ZygoteStartFailedEx(String s) {super(s);}
+ ZygoteStartFailedEx(Throwable cause) {super(cause);}
+}
+
+/**
+ * Tools for managing OS processes.
+ */
+public class Process {
+ private static final String LOG_TAG = "Process";
+
+ private static final String ZYGOTE_SOCKET = "zygote";
+
+ /**
+ * Name of a process for running the platform's media services.
+ * {@hide}
+ */
+ public static final String ANDROID_SHARED_MEDIA = "com.android.process.media";
+
+ /**
+ * Name of the process that Google content providers can share.
+ * {@hide}
+ */
+ public static final String GOOGLE_SHARED_APP_CONTENT = "com.google.process.content";
+
+ /**
+ * Defines the UID/GID under which system code runs.
+ */
+ public static final int SYSTEM_UID = 1000;
+
+ /**
+ * Defines the UID/GID under which the telephony code runs.
+ */
+ public static final int PHONE_UID = 1001;
+
+ /**
+ * Defines the start of a range of UIDs (and GIDs), going from this
+ * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
+ * to applications.
+ */
+ public static final int FIRST_APPLICATION_UID = 10000;
+ /**
+ * Last of application-specific UIDs starting at
+ * {@link #FIRST_APPLICATION_UID}.
+ */
+ public static final int LAST_APPLICATION_UID = 99999;
+
+ /**
+ * Defines a secondary group id for access to the bluetooth hardware.
+ */
+ public static final int BLUETOOTH_GID = 2000;
+
+ /**
+ * Standard priority of application threads.
+ * Use with {@link #setThreadPriority(int)} and
+ * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+ * {@link java.lang.Thread} class.
+ */
+ public static final int THREAD_PRIORITY_DEFAULT = 0;
+
+ /*
+ * ***************************************
+ * ** Keep in sync with utils/threads.h **
+ * ***************************************
+ */
+
+ /**
+ * Lowest available thread priority. Only for those who really, really
+ * don't want to run if anything else is happening.
+ * Use with {@link #setThreadPriority(int)} and
+ * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+ * {@link java.lang.Thread} class.
+ */
+ public static final int THREAD_PRIORITY_LOWEST = 19;
+
+ /**
+ * Standard priority background threads. This gives your thread a slightly
+ * lower than normal priority, so that it will have less chance of impacting
+ * the responsiveness of the user interface.
+ * Use with {@link #setThreadPriority(int)} and
+ * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+ * {@link java.lang.Thread} class.
+ */
+ public static final int THREAD_PRIORITY_BACKGROUND = 10;
+
+ /**
+ * Standard priority of threads that are currently running a user interface
+ * that the user is interacting with. Applications can not normally
+ * change to this priority; the system will automatically adjust your
+ * application threads as the user moves through the UI.
+ * Use with {@link #setThreadPriority(int)} and
+ * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+ * {@link java.lang.Thread} class.
+ */
+ public static final int THREAD_PRIORITY_FOREGROUND = -2;
+
+ /**
+ * Standard priority of system display threads, involved in updating
+ * the user interface. Applications can not
+ * normally change to this priority.
+ * Use with {@link #setThreadPriority(int)} and
+ * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+ * {@link java.lang.Thread} class.
+ */
+ public static final int THREAD_PRIORITY_DISPLAY = -4;
+
+ /**
+ * Standard priority of the most important display threads, for compositing
+ * the screen and retrieving input events. Applications can not normally
+ * change to this priority.
+ * Use with {@link #setThreadPriority(int)} and
+ * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+ * {@link java.lang.Thread} class.
+ */
+ public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8;
+
+ /**
+ * Standard priority of audio threads. Applications can not normally
+ * change to this priority.
+ * Use with {@link #setThreadPriority(int)} and
+ * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+ * {@link java.lang.Thread} class.
+ */
+ public static final int THREAD_PRIORITY_AUDIO = -16;
+
+ /**
+ * Standard priority of the most important audio threads.
+ * Applications can not normally change to this priority.
+ * Use with {@link #setThreadPriority(int)} and
+ * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+ * {@link java.lang.Thread} class.
+ */
+ public static final int THREAD_PRIORITY_URGENT_AUDIO = -19;
+
+ /**
+ * Minimum increment to make a priority more favorable.
+ */
+ public static final int THREAD_PRIORITY_MORE_FAVORABLE = -1;
+
+ /**
+ * Minimum increment to make a priority less favorable.
+ */
+ public static final int THREAD_PRIORITY_LESS_FAVORABLE = +1;
+
+ public static final int SIGNAL_QUIT = 3;
+ public static final int SIGNAL_KILL = 9;
+ public static final int SIGNAL_USR1 = 10;
+
+ // State for communicating with zygote process
+
+ static LocalSocket sZygoteSocket;
+ static DataInputStream sZygoteInputStream;
+ static BufferedWriter sZygoteWriter;
+
+ /** true if previous zygote open failed */
+ static boolean sPreviousZygoteOpenFailed;
+
+ /**
+ * Start a new process.
+ *
+ * <p>If processes are enabled, a new process is created and the
+ * static main() function of a <var>processClass</var> is executed there.
+ * The process will continue running after this function returns.
+ *
+ * <p>If processes are not enabled, a new thread in the caller's
+ * process is created and main() of <var>processClass</var> called there.
+ *
+ * <p>The niceName parameter, if not an empty string, is a custom name to
+ * give to the process instead of using processClass. This allows you to
+ * make easily identifyable processes even if you are using the same base
+ * <var>processClass</var> to start them.
+ *
+ * @param processClass The class to use as the process's main entry
+ * point.
+ * @param niceName A more readable name to use for the process.
+ * @param uid The user-id under which the process will run.
+ * @param gid The group-id under which the process will run.
+ * @param gids Additional group-ids associated with the process.
+ * @param enableDebugger True if debugging should be enabled for this process.
+ * @param zygoteArgs Additional arguments to supply to the zygote process.
+ *
+ * @return int If > 0 the pid of the new process; if 0 the process is
+ * being emulated by a thread
+ * @throws RuntimeException on fatal start failure
+ *
+ * {@hide}
+ */
+ public static final int start(final String processClass,
+ final String niceName,
+ int uid, int gid, int[] gids,
+ int debugFlags,
+ String[] zygoteArgs)
+ {
+ if (supportsProcesses()) {
+ try {
+ return startViaZygote(processClass, niceName, uid, gid, gids,
+ debugFlags, zygoteArgs);
+ } catch (ZygoteStartFailedEx ex) {
+ Log.e(LOG_TAG,
+ "Starting VM process through Zygote failed");
+ throw new RuntimeException(
+ "Starting VM process through Zygote failed", ex);
+ }
+ } else {
+ // Running in single-process mode
+
+ Runnable runnable = new Runnable() {
+ public void run() {
+ Process.invokeStaticMain(processClass);
+ }
+ };
+
+ // Thread constructors must not be called with null names (see spec).
+ if (niceName != null) {
+ new Thread(runnable, niceName).start();
+ } else {
+ new Thread(runnable).start();
+ }
+
+ return 0;
+ }
+ }
+
+ /**
+ * Start a new process. Don't supply a custom nice name.
+ * {@hide}
+ */
+ public static final int start(String processClass, int uid, int gid,
+ int[] gids, int debugFlags, String[] zygoteArgs) {
+ return start(processClass, "", uid, gid, gids,
+ debugFlags, zygoteArgs);
+ }
+
+ private static void invokeStaticMain(String className) {
+ Class cl;
+ Object args[] = new Object[1];
+
+ args[0] = new String[0]; //this is argv
+
+ try {
+ cl = Class.forName(className);
+ cl.getMethod("main", new Class[] { String[].class })
+ .invoke(null, args);
+ } catch (Exception ex) {
+ // can be: ClassNotFoundException,
+ // NoSuchMethodException, SecurityException,
+ // IllegalAccessException, IllegalArgumentException
+ // InvocationTargetException
+ // or uncaught exception from main()
+
+ Log.e(LOG_TAG, "Exception invoking static main on "
+ + className, ex);
+
+ throw new RuntimeException(ex);
+ }
+
+ }
+
+ /** retry interval for opening a zygote socket */
+ static final int ZYGOTE_RETRY_MILLIS = 500;
+
+ /**
+ * Tries to open socket to Zygote process if not already open. If
+ * already open, does nothing. May block and retry.
+ */
+ private static void openZygoteSocketIfNeeded()
+ throws ZygoteStartFailedEx {
+
+ int retryCount;
+
+ if (sPreviousZygoteOpenFailed) {
+ /*
+ * If we've failed before, expect that we'll fail again and
+ * don't pause for retries.
+ */
+ retryCount = 0;
+ } else {
+ retryCount = 10;
+ }
+
+ /*
+ * See bug #811181: Sometimes runtime can make it up before zygote.
+ * Really, we'd like to do something better to avoid this condition,
+ * but for now just wait a bit...
+ */
+ for (int retry = 0
+ ; (sZygoteSocket == null) && (retry < (retryCount + 1))
+ ; retry++ ) {
+
+ if (retry > 0) {
+ try {
+ Log.i("Zygote", "Zygote not up yet, sleeping...");
+ Thread.sleep(ZYGOTE_RETRY_MILLIS);
+ } catch (InterruptedException ex) {
+ // should never happen
+ }
+ }
+
+ try {
+ sZygoteSocket = new LocalSocket();
+
+ sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET,
+ LocalSocketAddress.Namespace.RESERVED));
+
+ sZygoteInputStream
+ = new DataInputStream(sZygoteSocket.getInputStream());
+
+ sZygoteWriter =
+ new BufferedWriter(
+ new OutputStreamWriter(
+ sZygoteSocket.getOutputStream()),
+ 256);
+
+ Log.i("Zygote", "Process: zygote socket opened");
+
+ sPreviousZygoteOpenFailed = false;
+ break;
+ } catch (IOException ex) {
+ if (sZygoteSocket != null) {
+ try {
+ sZygoteSocket.close();
+ } catch (IOException ex2) {
+ Log.e(LOG_TAG,"I/O exception on close after exception",
+ ex2);
+ }
+ }
+
+ sZygoteSocket = null;
+ }
+ }
+
+ if (sZygoteSocket == null) {
+ sPreviousZygoteOpenFailed = true;
+ throw new ZygoteStartFailedEx("connect failed");
+ }
+ }
+
+ /**
+ * Sends an argument list to the zygote process, which starts a new child
+ * and returns the child's pid. Please note: the present implementation
+ * replaces newlines in the argument list with spaces.
+ * @param args argument list
+ * @return PID of new child process
+ * @throws ZygoteStartFailedEx if process start failed for any reason
+ */
+ private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
+ throws ZygoteStartFailedEx {
+
+ int pid;
+
+ openZygoteSocketIfNeeded();
+
+ try {
+ /**
+ * See com.android.internal.os.ZygoteInit.readArgumentList()
+ * Presently the wire format to the zygote process is:
+ * a) a count of arguments (argc, in essence)
+ * b) a number of newline-separated argument strings equal to count
+ *
+ * After the zygote process reads these it will write the pid of
+ * the child or -1 on failure.
+ */
+
+ sZygoteWriter.write(Integer.toString(args.size()));
+ sZygoteWriter.newLine();
+
+ int sz = args.size();
+ for (int i = 0; i < sz; i++) {
+ String arg = args.get(i);
+ if (arg.indexOf('\n') >= 0) {
+ throw new ZygoteStartFailedEx(
+ "embedded newlines not allowed");
+ }
+ sZygoteWriter.write(arg);
+ sZygoteWriter.newLine();
+ }
+
+ sZygoteWriter.flush();
+
+ // Should there be a timeout on this?
+ pid = sZygoteInputStream.readInt();
+
+ if (pid < 0) {
+ throw new ZygoteStartFailedEx("fork() failed");
+ }
+ } catch (IOException ex) {
+ try {
+ if (sZygoteSocket != null) {
+ sZygoteSocket.close();
+ }
+ } catch (IOException ex2) {
+ // we're going to fail anyway
+ Log.e(LOG_TAG,"I/O exception on routine close", ex2);
+ }
+
+ sZygoteSocket = null;
+
+ throw new ZygoteStartFailedEx(ex);
+ }
+
+ return pid;
+ }
+
+ /**
+ * Starts a new process via the zygote mechanism.
+ *
+ * @param processClass Class name whose static main() to run
+ * @param niceName 'nice' process name to appear in ps
+ * @param uid a POSIX uid that the new process should setuid() to
+ * @param gid a POSIX gid that the new process shuold setgid() to
+ * @param gids null-ok; a list of supplementary group IDs that the
+ * new process should setgroup() to.
+ * @param enableDebugger True if debugging should be enabled for this process.
+ * @param extraArgs Additional arguments to supply to the zygote process.
+ * @return PID
+ * @throws ZygoteStartFailedEx if process start failed for any reason
+ */
+ private static int startViaZygote(final String processClass,
+ final String niceName,
+ final int uid, final int gid,
+ final int[] gids,
+ int debugFlags,
+ String[] extraArgs)
+ throws ZygoteStartFailedEx {
+ int pid;
+
+ synchronized(Process.class) {
+ ArrayList<String> argsForZygote = new ArrayList<String>();
+
+ // --runtime-init, --setuid=, --setgid=,
+ // and --setgroups= must go first
+ argsForZygote.add("--runtime-init");
+ argsForZygote.add("--setuid=" + uid);
+ argsForZygote.add("--setgid=" + gid);
+ if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
+ argsForZygote.add("--enable-debugger");
+ }
+ if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
+ argsForZygote.add("--enable-checkjni");
+ }
+ if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
+ argsForZygote.add("--enable-assert");
+ }
+
+ //TODO optionally enable debuger
+ //argsForZygote.add("--enable-debugger");
+
+ // --setgroups is a comma-separated list
+ if (gids != null && gids.length > 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("--setgroups=");
+
+ int sz = gids.length;
+ for (int i = 0; i < sz; i++) {
+ if (i != 0) {
+ sb.append(',');
+ }
+ sb.append(gids[i]);
+ }
+
+ argsForZygote.add(sb.toString());
+ }
+
+ if (niceName != null) {
+ argsForZygote.add("--nice-name=" + niceName);
+ }
+
+ argsForZygote.add(processClass);
+
+ if (extraArgs != null) {
+ for (String arg : extraArgs) {
+ argsForZygote.add(arg);
+ }
+ }
+
+ pid = zygoteSendArgsAndGetPid(argsForZygote);
+ }
+
+ if (pid <= 0) {
+ throw new ZygoteStartFailedEx("zygote start failed:" + pid);
+ }
+
+ return pid;
+ }
+
+ /**
+ * Returns elapsed milliseconds of the time this process has run.
+ * @return Returns the number of milliseconds this process has return.
+ */
+ public static final native long getElapsedCpuTime();
+
+ /**
+ * Returns the identifier of this process, which can be used with
+ * {@link #killProcess} and {@link #sendSignal}.
+ */
+ public static final native int myPid();
+
+ /**
+ * Returns the identifier of the calling thread, which be used with
+ * {@link #setThreadPriority(int, int)}.
+ */
+ public static final native int myTid();
+
+ /**
+ * Returns the identifier of this process's user.
+ */
+ public static final native int myUid();
+
+ /**
+ * Returns the UID assigned to a particular user name, or -1 if there is
+ * none. If the given string consists of only numbers, it is converted
+ * directly to a uid.
+ */
+ public static final native int getUidForName(String name);
+
+ /**
+ * Returns the GID assigned to a particular user name, or -1 if there is
+ * none. If the given string consists of only numbers, it is converted
+ * directly to a gid.
+ */
+ public static final native int getGidForName(String name);
+
+ /**
+ * Set the priority of a thread, based on Linux priorities.
+ *
+ * @param tid The identifier of the thread/process to change.
+ * @param priority A Linux priority level, from -20 for highest scheduling
+ * priority to 19 for lowest scheduling priority.
+ *
+ * @throws IllegalArgumentException Throws IllegalArgumentException if
+ * <var>tid</var> does not exist.
+ * @throws SecurityException Throws SecurityException if your process does
+ * not have permission to modify the given thread, or to use the given
+ * priority.
+ */
+ public static final native void setThreadPriority(int tid, int priority)
+ throws IllegalArgumentException, SecurityException;
+
+ /**
+ * Set the priority of the calling thread, based on Linux priorities. See
+ * {@link #setThreadPriority(int, int)} for more information.
+ *
+ * @param priority A Linux priority level, from -20 for highest scheduling
+ * priority to 19 for lowest scheduling priority.
+ *
+ * @throws IllegalArgumentException Throws IllegalArgumentException if
+ * <var>tid</var> does not exist.
+ * @throws SecurityException Throws SecurityException if your process does
+ * not have permission to modify the given thread, or to use the given
+ * priority.
+ *
+ * @see #setThreadPriority(int, int)
+ */
+ public static final native void setThreadPriority(int priority)
+ throws IllegalArgumentException, SecurityException;
+
+ /**
+ * Return the current priority of a thread, based on Linux priorities.
+ *
+ * @param tid The identifier of the thread/process to change.
+ *
+ * @return Returns the current priority, as a Linux priority level,
+ * from -20 for highest scheduling priority to 19 for lowest scheduling
+ * priority.
+ *
+ * @throws IllegalArgumentException Throws IllegalArgumentException if
+ * <var>tid</var> does not exist.
+ */
+ public static final native int getThreadPriority(int tid)
+ throws IllegalArgumentException;
+
+ /**
+ * Determine whether the current environment supports multiple processes.
+ *
+ * @return Returns true if the system can run in multiple processes, else
+ * false if everything is running in a single process.
+ */
+ public static final native boolean supportsProcesses();
+
+ /**
+ * Set the out-of-memory badness adjustment for a process.
+ *
+ * @param pid The process identifier to set.
+ * @param amt Adjustment value -- linux allows -16 to +15.
+ *
+ * @return Returns true if the underlying system supports this
+ * feature, else false.
+ *
+ * {@hide}
+ */
+ public static final native boolean setOomAdj(int pid, int amt);
+
+ /**
+ * Change this process's argv[0] parameter. This can be useful to show
+ * more descriptive information in things like the 'ps' command.
+ *
+ * @param text The new name of this process.
+ *
+ * {@hide}
+ */
+ public static final native void setArgV0(String text);
+
+ /**
+ * Kill the process with the given PID.
+ * Note that, though this API allows us to request to
+ * kill any process based on its PID, the kernel will
+ * still impose standard restrictions on which PIDs you
+ * are actually able to kill. Typically this means only
+ * the process running the caller's packages/application
+ * and any additional processes created by that app; packages
+ * sharing a common UID will also be able to kill each
+ * other's processes.
+ */
+ public static final void killProcess(int pid) {
+ sendSignal(pid, SIGNAL_KILL);
+ }
+
+ /** @hide */
+ public static final native int setUid(int uid);
+
+ /** @hide */
+ public static final native int setGid(int uid);
+
+ /**
+ * Send a signal to the given process.
+ *
+ * @param pid The pid of the target process.
+ * @param signal The signal to send.
+ */
+ public static final native void sendSignal(int pid, int signal);
+
+ /** @hide */
+ public static final native int getFreeMemory();
+
+ /** @hide */
+ public static final native void readProcLines(String path,
+ String[] reqFields, long[] outSizes);
+
+ /** @hide */
+ public static final native int[] getPids(String path, int[] lastArray);
+
+ /** @hide */
+ public static final int PROC_TERM_MASK = 0xff;
+ /** @hide */
+ public static final int PROC_ZERO_TERM = 0;
+ /** @hide */
+ public static final int PROC_SPACE_TERM = (int)' ';
+ /** @hide */
+ public static final int PROC_COMBINE = 0x100;
+ /** @hide */
+ public static final int PROC_PARENS = 0x200;
+ /** @hide */
+ public static final int PROC_OUT_STRING = 0x1000;
+ /** @hide */
+ public static final int PROC_OUT_LONG = 0x2000;
+ /** @hide */
+ public static final int PROC_OUT_FLOAT = 0x4000;
+
+ /** @hide */
+ public static final native boolean readProcFile(String file, int[] format,
+ String[] outStrings, long[] outLongs, float[] outFloats);
+
+ /**
+ * Gets the total Pss value for a given process, in bytes.
+ *
+ * @param pid the process to the Pss for
+ * @return the total Pss value for the given process in bytes,
+ * or -1 if the value cannot be determined
+ * @hide
+ */
+ public static final native long getPss(int pid);
+}
diff --git a/core/java/android/os/Registrant.java b/core/java/android/os/Registrant.java
new file mode 100644
index 0000000..c1780b9
--- /dev/null
+++ b/core/java/android/os/Registrant.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.os.Handler;
+import android.os.Message;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+
+/** @hide */
+public class Registrant
+{
+ public
+ Registrant(Handler h, int what, Object obj)
+ {
+ refH = new WeakReference(h);
+ this.what = what;
+ userObj = obj;
+ }
+
+ public void
+ clear()
+ {
+ refH = null;
+ userObj = null;
+ }
+
+ public void
+ notifyRegistrant()
+ {
+ internalNotifyRegistrant (null, null);
+ }
+
+ public void
+ notifyResult(Object result)
+ {
+ internalNotifyRegistrant (result, null);
+ }
+
+ public void
+ notifyException(Throwable exception)
+ {
+ internalNotifyRegistrant (null, exception);
+ }
+
+ /**
+ * This makes a copy of @param ar
+ */
+ public void
+ notifyRegistrant(AsyncResult ar)
+ {
+ internalNotifyRegistrant (ar.result, ar.exception);
+ }
+
+ /*package*/ void
+ internalNotifyRegistrant (Object result, Throwable exception)
+ {
+ Handler h = getHandler();
+
+ if (h == null) {
+ clear();
+ } else {
+ Message msg = Message.obtain();
+
+ msg.what = what;
+
+ msg.obj = new AsyncResult(userObj, result, exception);
+
+ h.sendMessage(msg);
+ }
+ }
+
+ /**
+ * NOTE: May return null if weak reference has been collected
+ */
+
+ public Message
+ messageForRegistrant()
+ {
+ Handler h = getHandler();
+
+ if (h == null) {
+ clear();
+
+ return null;
+ } else {
+ Message msg = h.obtainMessage();
+
+ msg.what = what;
+ msg.obj = userObj;
+
+ return msg;
+ }
+ }
+
+ public Handler
+ getHandler()
+ {
+ if (refH == null)
+ return null;
+
+ return (Handler) refH.get();
+ }
+
+ WeakReference refH;
+ int what;
+ Object userObj;
+}
+
diff --git a/core/java/android/os/RegistrantList.java b/core/java/android/os/RegistrantList.java
new file mode 100644
index 0000000..56b9e2b
--- /dev/null
+++ b/core/java/android/os/RegistrantList.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+import android.os.Handler;
+import android.os.Message;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/** @hide */
+public class RegistrantList
+{
+ ArrayList registrants = new ArrayList(); // of Registrant
+
+ public synchronized void
+ add(Handler h, int what, Object obj)
+ {
+ add(new Registrant(h, what, obj));
+ }
+
+ public synchronized void
+ addUnique(Handler h, int what, Object obj)
+ {
+ // if the handler is already in the registrant list, remove it
+ remove(h);
+ add(new Registrant(h, what, obj));
+ }
+
+ public synchronized void
+ add(Registrant r)
+ {
+ removeCleared();
+ registrants.add(r);
+ }
+
+ public synchronized void
+ removeCleared()
+ {
+ for (int i = registrants.size() - 1; i >= 0 ; i--) {
+ Registrant r = (Registrant) registrants.get(i);
+
+ if (r.refH == null) {
+ registrants.remove(i);
+ }
+ }
+ }
+
+ public synchronized int
+ size()
+ {
+ return registrants.size();
+ }
+
+ public synchronized Object
+ get(int index)
+ {
+ return registrants.get(index);
+ }
+
+ private synchronized void
+ internalNotifyRegistrants (Object result, Throwable exception)
+ {
+ for (int i = 0, s = registrants.size(); i < s ; i++) {
+ Registrant r = (Registrant) registrants.get(i);
+ r.internalNotifyRegistrant(result, exception);
+ }
+ }
+
+ public /*synchronized*/ void
+ notifyRegistrants()
+ {
+ internalNotifyRegistrants(null, null);
+ }
+
+ public /*synchronized*/ void
+ notifyException(Throwable exception)
+ {
+ internalNotifyRegistrants (null, exception);
+ }
+
+ public /*synchronized*/ void
+ notifyResult(Object result)
+ {
+ internalNotifyRegistrants (result, null);
+ }
+
+
+ public /*synchronized*/ void
+ notifyRegistrants(AsyncResult ar)
+ {
+ internalNotifyRegistrants(ar.result, ar.exception);
+ }
+
+ public synchronized void
+ remove(Handler h)
+ {
+ for (int i = 0, s = registrants.size() ; i < s ; i++) {
+ Registrant r = (Registrant) registrants.get(i);
+ Handler rh;
+
+ rh = r.getHandler();
+
+ /* Clean up both the requested registrant and
+ * any now-collected registrants
+ */
+ if (rh == null || rh == h) {
+ r.clear();
+ }
+ }
+
+ removeCleared();
+ }
+}
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
new file mode 100644
index 0000000..04e7ef0
--- /dev/null
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2008 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 android.os;
+
+import java.util.HashMap;
+
+/**
+ * Takes care of the grunt work of maintaining a list of remote interfaces,
+ * typically for the use of performing callbacks from a
+ * {@link android.app.Service} to its clients. In particular, this:
+ *
+ * <ul>
+ * <li> Keeps track of a set of registered {@link IInterface} callbacks,
+ * taking care to identify them through their underlying unique {@link IBinder}
+ * (by calling {@link IInterface#asBinder IInterface.asBinder()}.
+ * <li> Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to
+ * each registered interface, so that it can be cleaned out of the list if its
+ * process goes away.
+ * <li> Performs locking of the underlying list of interfaces to deal with
+ * multithreaded incoming calls, and a thread-safe way to iterate over a
+ * snapshot of the list without holding its lock.
+ * </ul>
+ *
+ * <p>To use this class, simply create a single instance along with your
+ * service, and call its {@link #register} and {@link #unregister} methods
+ * as client register and unregister with your service. To call back on to
+ * the registered clients, use {@link #beginBroadcast},
+ * {@link #getBroadcastItem}, and {@link #finishBroadcast}.
+ *
+ * <p>If a registered callback's process goes away, this class will take
+ * care of automatically removing it from the list. If you want to do
+ * additional work in this situation, you can create a subclass that
+ * implements the {@link #onCallbackDied} method.
+ */
+public class RemoteCallbackList<E extends IInterface> {
+ /*package*/ HashMap<IBinder, Callback> mCallbacks
+ = new HashMap<IBinder, Callback>();
+ private IInterface[] mActiveBroadcast;
+ private boolean mKilled = false;
+
+ private final class Callback implements IBinder.DeathRecipient {
+ final E mCallback;
+
+ Callback(E callback) {
+ mCallback = callback;
+ }
+
+ public void binderDied() {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(mCallback.asBinder());
+ }
+ onCallbackDied(mCallback);
+ }
+ }
+
+ /**
+ * Add a new callback to the list. This callback will remain in the list
+ * until a corresponding call to {@link #unregister} or its hosting process
+ * goes away. If the callback was already registered (determined by
+ * checking to see if the {@link IInterface#asBinder callback.asBinder()}
+ * object is already in the list), then it will be left as-is.
+ * Registrations are not counted; a single call to {@link #unregister}
+ * will remove a callback after any number calls to register it.
+ *
+ * @param callback The callback interface to be added to the list. Must
+ * not be null -- passing null here will cause a NullPointerException.
+ * Most services will want to check for null before calling this with
+ * an object given from a client, so that clients can't crash the
+ * service with bad data.
+ *
+ * @return Returns true if the callback was successfully added to the list.
+ * Returns false if it was not added, either because {@link #kill} had
+ * previously been called or the callback's process has gone away.
+ *
+ * @see #unregister
+ * @see #kill
+ * @see #onCallbackDied
+ */
+ public boolean register(E callback) {
+ synchronized (mCallbacks) {
+ if (mKilled) {
+ return false;
+ }
+ IBinder binder = callback.asBinder();
+ try {
+ Callback cb = new Callback(callback);
+ binder.linkToDeath(cb, 0);
+ mCallbacks.put(binder, cb);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Remove from the list a callback that was previously added with
+ * {@link #register}. This uses the
+ * {@link IInterface#asBinder callback.asBinder()} object to correctly
+ * find the previous registration.
+ * Registrations are not counted; a single unregister call will remove
+ * a callback after any number calls to {@link #register} for it.
+ *
+ * @param callback The callback to be removed from the list. Passing
+ * null here will cause a NullPointerException, so you will generally want
+ * to check for null before calling.
+ *
+ * @return Returns true if the callback was found and unregistered. Returns
+ * false if the given callback was not found on the list.
+ *
+ * @see #register
+ */
+ public boolean unregister(E callback) {
+ synchronized (mCallbacks) {
+ Callback cb = mCallbacks.remove(callback.asBinder());
+ if (cb != null) {
+ cb.mCallback.asBinder().unlinkToDeath(cb, 0);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Disable this callback list. All registered callbacks are unregistered,
+ * and the list is disabled so that future calls to {@link #register} will
+ * fail. This should be used when a Service is stopping, to prevent clients
+ * from registering callbacks after it is stopped.
+ *
+ * @see #register
+ */
+ public void kill() {
+ synchronized (mCallbacks) {
+ for (Callback cb : mCallbacks.values()) {
+ cb.mCallback.asBinder().unlinkToDeath(cb, 0);
+ }
+ mCallbacks.clear();
+ mKilled = true;
+ }
+ }
+
+ /**
+ * Called when the process hosting a callback in the list has gone away.
+ * The default implementation does nothing.
+ *
+ * @param callback The callback whose process has died. Note that, since
+ * its process has died, you can not make any calls on to this interface.
+ * You can, however, retrieve its IBinder and compare it with another
+ * IBinder to see if it is the same object.
+ *
+ * @see #register
+ */
+ public void onCallbackDied(E callback) {
+ }
+
+ /**
+ * Prepare to start making calls to the currently registered callbacks.
+ * This creates a copy of the callback list, which you can retrieve items
+ * from using {@link #getBroadcastItem}. Note that only one broadcast can
+ * be active at a time, so you must be sure to always call this from the
+ * same thread (usually by scheduling with {@link Handler} or
+ * do your own synchronization. You must call {@link #finishBroadcast}
+ * when done.
+ *
+ * <p>A typical loop delivering a broadcast looks like this:
+ *
+ * <pre>
+ * final int N = callbacks.beginBroadcast();
+ * for (int i=0; i<N; i++) {
+ * try {
+ * callbacks.getBroadcastItem(i).somethingHappened();
+ * } catch (RemoteException e) {
+ * // The RemoteCallbackList will take care of removing
+ * // the dead object for us.
+ * }
+ * }
+ * callbacks.finishBroadcast();</pre>
+ *
+ * @return Returns the number of callbacks in the broadcast, to be used
+ * with {@link #getBroadcastItem} to determine the range of indices you
+ * can supply.
+ *
+ * @see #getBroadcastItem
+ * @see #finishBroadcast
+ */
+ public int beginBroadcast() {
+ synchronized (mCallbacks) {
+ final int N = mCallbacks.size();
+ if (N <= 0) {
+ return 0;
+ }
+ IInterface[] active = mActiveBroadcast;
+ if (active == null || active.length < N) {
+ mActiveBroadcast = active = new IInterface[N];
+ }
+ int i=0;
+ for (Callback cb : mCallbacks.values()) {
+ active[i++] = cb.mCallback;
+ }
+ return N;
+ }
+ }
+
+ /**
+ * Retrieve an item in the active broadcast that was previously started
+ * with {@link #beginBroadcast}. This can <em>only</em> be called after
+ * the broadcast is started, and its data is no longer valid after
+ * calling {@link #finishBroadcast}.
+ *
+ * <p>Note that it is possible for the process of one of the returned
+ * callbacks to go away before you call it, so you will need to catch
+ * {@link RemoteException} when calling on to the returned object.
+ * The callback list itself, however, will take care of unregistering
+ * these objects once it detects that it is no longer valid, so you can
+ * handle such an exception by simply ignoring it.
+ *
+ * @param index Which of the registered callbacks you would like to
+ * retrieve. Ranges from 0 to 1-{@link #beginBroadcast}.
+ *
+ * @return Returns the callback interface that you can call. This will
+ * always be non-null.
+ *
+ * @see #beginBroadcast
+ */
+ public E getBroadcastItem(int index) {
+ return (E)mActiveBroadcast[index];
+ }
+
+ /**
+ * Clean up the state of a broadcast previously initiated by calling
+ * {@link #beginBroadcast}. This must always be called when you are done
+ * with a broadcast.
+ *
+ * @see #beginBroadcast
+ */
+ public void finishBroadcast() {
+ IInterface[] active = mActiveBroadcast;
+ if (active != null) {
+ final int N = active.length;
+ for (int i=0; i<N; i++) {
+ active[i] = null;
+ }
+ }
+ }
+}
diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java
new file mode 100644
index 0000000..9d76156
--- /dev/null
+++ b/core/java/android/os/RemoteException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+import android.util.AndroidException;
+
+/**
+ * Parent exception for all Binder remote-invocation errors
+ */
+public class RemoteException extends AndroidException {
+ public RemoteException() {
+ super();
+ }
+}
diff --git a/core/java/android/os/RemoteMailException.java b/core/java/android/os/RemoteMailException.java
new file mode 100644
index 0000000..1ac96d1
--- /dev/null
+++ b/core/java/android/os/RemoteMailException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/** @hide */
+public class RemoteMailException extends Exception
+{
+ public RemoteMailException()
+ {
+ }
+
+ public RemoteMailException(String s)
+ {
+ super(s);
+ }
+}
+
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
new file mode 100644
index 0000000..b721665
--- /dev/null
+++ b/core/java/android/os/ServiceManager.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import com.android.internal.os.BinderInternal;
+
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** @hide */
+public final class ServiceManager {
+ private static final String TAG = "ServiceManager";
+
+ private static IServiceManager sServiceManager;
+ private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
+
+ private static IServiceManager getIServiceManager() {
+ if (sServiceManager != null) {
+ return sServiceManager;
+ }
+
+ // Find the service manager
+ sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
+ return sServiceManager;
+ }
+
+ /**
+ * Returns a reference to a service with the given name.
+ *
+ * @param name the name of the service to get
+ * @return a reference to the service, or <code>null</code> if the service doesn't exist
+ */
+ public static IBinder getService(String name) {
+ try {
+ IBinder service = sCache.get(name);
+ if (service != null) {
+ return service;
+ } else {
+ return getIServiceManager().getService(name);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in getService", e);
+ }
+ return null;
+ }
+
+ /**
+ * Place a new @a service called @a name into the service
+ * manager.
+ *
+ * @param name the name of the new service
+ * @param service the service object
+ */
+ public static void addService(String name, IBinder service) {
+ try {
+ getIServiceManager().addService(name, service);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in addService", e);
+ }
+ }
+
+ /**
+ * Retrieve an existing service called @a name from the
+ * service manager. Non-blocking.
+ */
+ public static IBinder checkService(String name) {
+ try {
+ IBinder service = sCache.get(name);
+ if (service != null) {
+ return service;
+ } else {
+ return getIServiceManager().checkService(name);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in checkService", e);
+ return null;
+ }
+ }
+
+ /**
+ * Return a list of all currently running services.
+ */
+ public static String[] listServices() throws RemoteException {
+ try {
+ return getIServiceManager().listServices();
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in listServices", e);
+ return null;
+ }
+ }
+
+ /**
+ * This is only intended to be called when the process is first being brought
+ * up and bound by the activity manager. There is only one thread in the process
+ * at that time, so no locking is done.
+ *
+ * @param cache the cache of service references
+ * @hide
+ */
+ public static void initServiceCache(Map<String, IBinder> cache) {
+ if (sCache.size() != 0 && Process.supportsProcesses()) {
+ throw new IllegalStateException("setServiceCache may only be called once");
+ }
+ sCache.putAll(cache);
+ }
+}
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
new file mode 100644
index 0000000..2aab0e6
--- /dev/null
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+
+/**
+ * Native implementation of the service manager. Most clients will only
+ * care about getDefault() and possibly asInterface().
+ * @hide
+ */
+public abstract class ServiceManagerNative extends Binder implements IServiceManager
+{
+ /**
+ * Cast a Binder object into a service manager interface, generating
+ * a proxy if needed.
+ */
+ static public IServiceManager asInterface(IBinder obj)
+ {
+ if (obj == null) {
+ return null;
+ }
+ IServiceManager in =
+ (IServiceManager)obj.queryLocalInterface(descriptor);
+ if (in != null) {
+ return in;
+ }
+
+ return new ServiceManagerProxy(obj);
+ }
+
+ public ServiceManagerNative()
+ {
+ attachInterface(this, descriptor);
+ }
+
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ {
+ try {
+ switch (code) {
+ case IServiceManager.GET_SERVICE_TRANSACTION: {
+ data.enforceInterface(IServiceManager.descriptor);
+ String name = data.readString();
+ IBinder service = getService(name);
+ reply.writeStrongBinder(service);
+ return true;
+ }
+
+ case IServiceManager.CHECK_SERVICE_TRANSACTION: {
+ data.enforceInterface(IServiceManager.descriptor);
+ String name = data.readString();
+ IBinder service = checkService(name);
+ reply.writeStrongBinder(service);
+ return true;
+ }
+
+ case IServiceManager.ADD_SERVICE_TRANSACTION: {
+ data.enforceInterface(IServiceManager.descriptor);
+ String name = data.readString();
+ IBinder service = data.readStrongBinder();
+ addService(name, service);
+ return true;
+ }
+
+ case IServiceManager.LIST_SERVICES_TRANSACTION: {
+ data.enforceInterface(IServiceManager.descriptor);
+ String[] list = listServices();
+ reply.writeStringArray(list);
+ return true;
+ }
+
+ case IServiceManager.SET_PERMISSION_CONTROLLER_TRANSACTION: {
+ data.enforceInterface(IServiceManager.descriptor);
+ IPermissionController controller
+ = IPermissionController.Stub.asInterface(
+ data.readStrongBinder());
+ setPermissionController(controller);
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ }
+
+ return false;
+ }
+
+ public IBinder asBinder()
+ {
+ return this;
+ }
+}
+
+class ServiceManagerProxy implements IServiceManager {
+ public ServiceManagerProxy(IBinder remote) {
+ mRemote = remote;
+ }
+
+ public IBinder asBinder() {
+ return mRemote;
+ }
+
+ public IBinder getService(String name) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IServiceManager.descriptor);
+ data.writeString(name);
+ mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
+ IBinder binder = reply.readStrongBinder();
+ reply.recycle();
+ data.recycle();
+ return binder;
+ }
+
+ public IBinder checkService(String name) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IServiceManager.descriptor);
+ data.writeString(name);
+ mRemote.transact(CHECK_SERVICE_TRANSACTION, data, reply, 0);
+ IBinder binder = reply.readStrongBinder();
+ reply.recycle();
+ data.recycle();
+ return binder;
+ }
+
+ public void addService(String name, IBinder service)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IServiceManager.descriptor);
+ data.writeString(name);
+ data.writeStrongBinder(service);
+ mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
+ reply.recycle();
+ data.recycle();
+ }
+
+ public String[] listServices() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IServiceManager.descriptor);
+ mRemote.transact(LIST_SERVICES_TRANSACTION, data, reply, 0);
+ String[] list = reply.readStringArray();
+ reply.recycle();
+ data.recycle();
+ return list;
+ }
+
+ public void setPermissionController(IPermissionController controller)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IServiceManager.descriptor);
+ data.writeStrongBinder(controller.asBinder());
+ mRemote.transact(SET_PERMISSION_CONTROLLER_TRANSACTION, data, reply, 0);
+ reply.recycle();
+ data.recycle();
+ }
+
+ private IBinder mRemote;
+}
diff --git a/core/java/android/os/StatFs.java b/core/java/android/os/StatFs.java
new file mode 100644
index 0000000..912bfdf
--- /dev/null
+++ b/core/java/android/os/StatFs.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Retrieve overall information about the space on a filesystem. This is a
+ * Wrapper for Unix statfs().
+ */
+public class StatFs {
+ /**
+ * Construct a new StatFs for looking at the stats of the
+ * filesystem at <var>path</var>. Upon construction, the stat of
+ * the file system will be performed, and the values retrieved available
+ * from the methods on this class.
+ *
+ * @param path A path in the desired file system to state.
+ */
+ public StatFs(String path) { native_setup(path); }
+
+ /**
+ * Perform a restat of the file system referenced by this object. This
+ * is the same as re-constructing the object with the same file system
+ * path, and the new stat values are available upon return.
+ */
+ public void restat(String path) { native_restat(path); }
+
+ @Override
+ protected void finalize() { native_finalize(); }
+
+ /**
+ * The size, in bytes, of a block on the file system. This corresponds
+ * to the Unix statfs.f_bsize field.
+ */
+ public native int getBlockSize();
+
+ /**
+ * The total number of blocks on the file system. This corresponds
+ * to the Unix statfs.f_blocks field.
+ */
+ public native int getBlockCount();
+
+ /**
+ * The total number of blocks that are free on the file system, including
+ * reserved blocks (that are not available to normal applications). This
+ * corresponds to the Unix statfs.f_bfree field. Most applications will
+ * want to use {@link #getAvailableBlocks()} instead.
+ */
+ public native int getFreeBlocks();
+
+ /**
+ * The number of blocks that are free on the file system and available to
+ * applications. This corresponds to the Unix statfs.f_bavail field.
+ */
+ public native int getAvailableBlocks();
+
+ private int mNativeContext;
+ private native void native_restat(String path);
+ private native void native_setup(String path);
+ private native void native_finalize();
+}
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
new file mode 100644
index 0000000..2b57b39
--- /dev/null
+++ b/core/java/android/os/SystemClock.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+
+/**
+ * Core timekeeping facilities.
+ *
+ * <p> Three different clocks are available, and they should not be confused:
+ *
+ * <ul>
+ * <li> <p> {@link System#currentTimeMillis System.currentTimeMillis()}
+ * is the standard "wall" clock (time and date) expressing milliseconds
+ * since the epoch. The wall clock can be set by the user or the phone
+ * network (see {@link #setCurrentTimeMillis}), so the time may jump
+ * backwards or forwards unpredictably. This clock should only be used
+ * when correspondence with real-world dates and times is important, such
+ * as in a calendar or alarm clock application. Interval or elapsed
+ * time measurements should use a different clock.
+ *
+ * <li> <p> {@link #uptimeMillis} is counted in milliseconds since the
+ * system was booted. This clock stops when the system enters deep
+ * sleep (CPU off, display dark, device waiting for external input),
+ * but is not affected by clock scaling, idle, or other power saving
+ * mechanisms. This is the basis for most interval timing
+ * such as {@link Thread#sleep(long) Thread.sleep(millls)},
+ * {@link Object#wait(long) Object.wait(millis)}, and
+ * {@link System#nanoTime System.nanoTime()}. This clock is guaranteed
+ * to be monotonic, and is the recommended basis for the general purpose
+ * interval timing of user interface events, performance measurements,
+ * and anything else that does not need to measure elapsed time during
+ * device sleep. Most methods that accept a timestamp value expect the
+ * {@link #uptimeMillis} clock.
+ *
+ * <li> <p> {@link #elapsedRealtime} is counted in milliseconds since the
+ * system was booted, including deep sleep. This clock should be used
+ * when measuring time intervals that may span periods of system sleep.
+ * </ul>
+ *
+ * There are several mechanisms for controlling the timing of events:
+ *
+ * <ul>
+ * <li> <p> Standard functions like {@link Thread#sleep(long)
+ * Thread.sleep(millis)} and {@link Object#wait(long) Object.wait(millis)}
+ * are always available. These functions use the {@link #uptimeMillis}
+ * clock; if the device enters sleep, the remainder of the time will be
+ * postponed until the device wakes up. These synchronous functions may
+ * be interrupted with {@link Thread#interrupt Thread.interrupt()}, and
+ * you must handle {@link InterruptedException}.
+ *
+ * <li> <p> {@link #sleep SystemClock.sleep(millis)} is a utility function
+ * very similar to {@link Thread#sleep(long) Thread.sleep(millis)}, but it
+ * ignores {@link InterruptedException}. Use this function for delays if
+ * you do not use {@link Thread#interrupt Thread.interrupt()}, as it will
+ * preserve the interrupted state of the thread.
+ *
+ * <li> <p> The {@link android.os.Handler} class can schedule asynchronous
+ * callbacks at an absolute or relative time. Handler objects also use the
+ * {@link #uptimeMillis} clock, and require an {@link android.os.Looper
+ * event loop} (normally present in any GUI application).
+ *
+ * <li> <p> The {@link android.app.AlarmManager} can trigger one-time or
+ * recurring events which occur even when the device is in deep sleep
+ * or your application is not running. Events may be scheduled with your
+ * choice of {@link java.lang.System#currentTimeMillis} (RTC) or
+ * {@link #elapsedRealtime} (ELAPSED_REALTIME), and cause an
+ * {@link android.content.Intent} broadcast when they occur.
+ * </ul>
+ */
+public final class SystemClock {
+ /**
+ * This class is uninstantiable.
+ */
+ private SystemClock() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Waits a given number of milliseconds (of uptimeMillis) before returning.
+ * Similar to {@link java.lang.Thread#sleep(long)}, but does not throw
+ * {@link InterruptedException}; {@link Thread#interrupt()} events are
+ * deferred until the next interruptible operation. Does not return until
+ * at least the specified number of milliseconds has elapsed.
+ *
+ * @param ms to sleep before returning, in milliseconds of uptime.
+ */
+ public static void sleep(long ms)
+ {
+ long start = uptimeMillis();
+ long duration = ms;
+ boolean interrupted = false;
+ do {
+ try {
+ Thread.sleep(duration);
+ }
+ catch (InterruptedException e) {
+ interrupted = true;
+ }
+ duration = start + ms - uptimeMillis();
+ } while (duration > 0);
+
+ if (interrupted) {
+ // Important: we don't want to quietly eat an interrupt() event,
+ // so we make sure to re-interrupt the thread so that the next
+ // call to Thread.sleep() or Object.wait() will be interrupted.
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /**
+ * Sets the current wall time, in milliseconds. Requires the calling
+ * process to have appropriate permissions.
+ *
+ * @return if the clock was successfully set to the specified time.
+ */
+ native public static boolean setCurrentTimeMillis(long millis);
+
+ /**
+ * Returns milliseconds since boot, not counting time spent in deep sleep.
+ * <b>Note:</b> This value may get reset occasionally (before it would
+ * otherwise wrap around).
+ *
+ * @return milliseconds of non-sleep uptime since boot.
+ */
+ native public static long uptimeMillis();
+
+ /**
+ * Returns milliseconds since boot, including time spent in sleep.
+ *
+ * @return elapsed milliseconds since boot.
+ */
+ native public static long elapsedRealtime();
+
+ /**
+ * Returns milliseconds running in the current thread.
+ *
+ * @return elapsed milliseconds in the thread
+ */
+ public static native long currentThreadTimeMillis();
+}
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
new file mode 100644
index 0000000..c3ae3c2
--- /dev/null
+++ b/core/java/android/os/SystemProperties.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+
+/**
+ * Gives access to the system properties store. The system properties
+ * store contains a list of string key-value pairs.
+ *
+ * {@hide}
+ */
+public class SystemProperties
+{
+ public static final int PROP_NAME_MAX = 31;
+ public static final int PROP_VALUE_MAX = 91;
+
+ private static native String native_get(String key);
+ private static native String native_get(String key, String def);
+ private static native void native_set(String key, String def);
+
+ /**
+ * Get the value for the given key.
+ * @return an empty string if the key isn't found
+ * @throws IllegalArgumentException if the key exceeds 32 characters
+ */
+ public static String get(String key) {
+ if (key.length() > PROP_NAME_MAX) {
+ throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+ }
+ return native_get(key);
+ }
+
+ /**
+ * Get the value for the given key.
+ * @return if the key isn't found, return def if it isn't null, or an empty string otherwise
+ * @throws IllegalArgumentException if the key exceeds 32 characters
+ */
+ public static String get(String key, String def) {
+ if (key.length() > PROP_NAME_MAX) {
+ throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+ }
+ return native_get(key, def);
+ }
+
+ /**
+ * Get the value for the given key, and return as an integer.
+ * @param key the key to lookup
+ * @param def a default value to return
+ * @return the key parsed as an integer, or def if the key isn't found or
+ * cannot be parsed
+ * @throws IllegalArgumentException if the key exceeds 32 characters
+ */
+ public static int getInt(String key, int def) {
+ try {
+ return Integer.parseInt(get(key));
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ /**
+ * Get the value for the given key, and return as a long.
+ * @param key the key to lookup
+ * @param def a default value to return
+ * @return the key parsed as a long, or def if the key isn't found or
+ * cannot be parsed
+ * @throws IllegalArgumentException if the key exceeds 32 characters
+ */
+ public static long getLong(String key, long def) {
+ try {
+ return Long.parseLong(get(key));
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ /**
+ * Get the value for the given key, returned as a boolean.
+ * Values 'n', 'no', '0', 'false' or 'off' are considered false.
+ * Values 'y', 'yes', '1', 'true' or 'on' are considered true.
+ * (case insensitive).
+ * If the key does not exist, or has any other value, then the default
+ * result is returned.
+ * @param key the key to lookup
+ * @param def a default value to return
+ * @return the key parsed as a boolean, or def if the key isn't found or is
+ * not able to be parsed as a boolean.
+ * @throws IllegalArgumentException if the key exceeds 32 characters
+ */
+ public static boolean getBoolean(String key, boolean def) {
+ String value = get(key);
+ // Deal with these quick cases first: not found, 0 and 1
+ if (value.equals("")) {
+ return def;
+ } else if (value.equals("0")) {
+ return false;
+ } else if (value.equals("1")) {
+ return true;
+ // now for slower (and hopefully less common) cases
+ } else if (value.equalsIgnoreCase("n") ||
+ value.equalsIgnoreCase("no") ||
+ value.equalsIgnoreCase("false") ||
+ value.equalsIgnoreCase("off")) {
+ return false;
+ } else if (value.equalsIgnoreCase("y") ||
+ value.equalsIgnoreCase("yes") ||
+ value.equalsIgnoreCase("true") ||
+ value.equalsIgnoreCase("on")) {
+ return true;
+ }
+ return def;
+ }
+
+ /**
+ * Set the value for the given key.
+ * @throws IllegalArgumentException if the key exceeds 32 characters
+ * @throws IllegalArgumentException if the value exceeds 92 characters
+ */
+ public static void set(String key, String val) {
+ if (key.length() > PROP_NAME_MAX) {
+ throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+ }
+ if (val != null && val.length() > PROP_VALUE_MAX) {
+ throw new IllegalArgumentException("val.length > " +
+ PROP_VALUE_MAX);
+ }
+ native_set(key, val);
+ }
+}
diff --git a/core/java/android/os/SystemService.java b/core/java/android/os/SystemService.java
new file mode 100644
index 0000000..447cd1f
--- /dev/null
+++ b/core/java/android/os/SystemService.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/** @hide */
+public class SystemService
+{
+ /** Request that the init daemon start a named service. */
+ public static void start(String name) {
+ SystemProperties.set("ctl.start", name);
+ }
+
+ /** Request that the init daemon stop a named service. */
+ public static void stop(String name) {
+ SystemProperties.set("ctl.stop", name);
+ }
+}
diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java
new file mode 100755
index 0000000..ac3cc92
--- /dev/null
+++ b/core/java/android/os/TokenWatcher.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.util.WeakHashMap;
+import java.util.Set;
+import android.util.Log;
+
+/**
+ * Helper class that helps you use IBinder objects as reference counted
+ * tokens. IBinders make good tokens because we find out when they are
+ * removed
+ *
+ */
+public abstract class TokenWatcher
+{
+ /**
+ * Construct the TokenWatcher
+ *
+ * @param h A handler to call {@link #acquired} and {@link #released}
+ * on. If you don't care, just call it like this, although your thread
+ * will have to be a Looper thread.
+ * <code>new TokenWatcher(new Handler())</code>
+ * @param tag A debugging tag for this TokenWatcher
+ */
+ public TokenWatcher(Handler h, String tag)
+ {
+ mHandler = h;
+ mTag = tag != null ? tag : "TokenWatcher";
+ }
+
+ /**
+ * Called when the number of active tokens goes from 0 to 1.
+ */
+ public abstract void acquired();
+
+ /**
+ * Called when the number of active tokens goes from 1 to 0.
+ */
+ public abstract void released();
+
+ /**
+ * Record that this token has been acquired. When acquire is called, and
+ * the current count is 0, the acquired method is called on the given
+ * handler.
+ *
+ * @param token An IBinder object. If this token has already been acquired,
+ * no action is taken.
+ * @param tag A string used by the {@link #dump} method for debugging,
+ * to see who has references.
+ */
+ public void acquire(IBinder token, String tag)
+ {
+ synchronized (mTokens) {
+ // explicitly checked to avoid bogus sendNotification calls because
+ // of the WeakHashMap and the GC
+ int oldSize = mTokens.size();
+
+ Death d = new Death(token, tag);
+ try {
+ token.linkToDeath(d, 0);
+ } catch (RemoteException e) {
+ return;
+ }
+ mTokens.put(token, d);
+
+ if (oldSize == 0 && !mAcquired) {
+ sendNotificationLocked(true);
+ mAcquired = true;
+ }
+ }
+ }
+
+ public void cleanup(IBinder token, boolean unlink)
+ {
+ synchronized (mTokens) {
+ Death d = mTokens.remove(token);
+ if (unlink && d != null) {
+ d.token.unlinkToDeath(d, 0);
+ d.token = null;
+ }
+
+ if (mTokens.size() == 0 && mAcquired) {
+ sendNotificationLocked(false);
+ mAcquired = false;
+ }
+ }
+ }
+
+ public void release(IBinder token)
+ {
+ cleanup(token, true);
+ }
+
+ public boolean isAcquired()
+ {
+ synchronized (mTokens) {
+ return mAcquired;
+ }
+ }
+
+ public void dump()
+ {
+ synchronized (mTokens) {
+ Set<IBinder> keys = mTokens.keySet();
+ Log.i(mTag, "Token count: " + mTokens.size());
+ int i = 0;
+ for (IBinder b: keys) {
+ Log.i(mTag, "[" + i + "] " + mTokens.get(b).tag + " - " + b);
+ i++;
+ }
+ }
+ }
+
+ private Runnable mNotificationTask = new Runnable() {
+ public void run()
+ {
+ int value;
+ synchronized (mTokens) {
+ value = mNotificationQueue;
+ mNotificationQueue = -1;
+ }
+ if (value == 1) {
+ acquired();
+ }
+ else if (value == 0) {
+ released();
+ }
+ }
+ };
+
+ private void sendNotificationLocked(boolean on)
+ {
+ int value = on ? 1 : 0;
+ if (mNotificationQueue == -1) {
+ // empty
+ mNotificationQueue = value;
+ mHandler.post(mNotificationTask);
+ }
+ else if (mNotificationQueue != value) {
+ // it's a pair, so cancel it
+ mNotificationQueue = -1;
+ mHandler.removeCallbacks(mNotificationTask);
+ }
+ // else, same so do nothing -- maybe we should warn?
+ }
+
+ private class Death implements IBinder.DeathRecipient
+ {
+ IBinder token;
+ String tag;
+
+ Death(IBinder token, String tag)
+ {
+ this.token = token;
+ this.tag = tag;
+ }
+
+ public void binderDied()
+ {
+ cleanup(token, false);
+ }
+
+ protected void finalize() throws Throwable
+ {
+ try {
+ if (token != null) {
+ Log.w(mTag, "cleaning up leaked reference: " + tag);
+ release(token);
+ }
+ }
+ finally {
+ super.finalize();
+ }
+ }
+ }
+
+ private WeakHashMap<IBinder,Death> mTokens = new WeakHashMap<IBinder,Death>();
+ private Handler mHandler;
+ private String mTag;
+ private int mNotificationQueue = -1;
+ private volatile boolean mAcquired = false;
+}
diff --git a/core/java/android/os/UEventObserver.java b/core/java/android/os/UEventObserver.java
new file mode 100644
index 0000000..b924e84
--- /dev/null
+++ b/core/java/android/os/UEventObserver.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2008 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 android.os;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * UEventObserver is an abstract class that receives UEvent's from the kernel.<p>
+ *
+ * Subclass UEventObserver, implementing onUEvent(UEvent event), then call
+ * startObserving() with a match string. The UEvent thread will then call your
+ * onUEvent() method when a UEvent occurs that contains your match string.<p>
+ *
+ * Call stopObserving() to stop receiving UEvent's.<p>
+ *
+ * There is only one UEvent thread per process, even if that process has
+ * multiple UEventObserver subclass instances. The UEvent thread starts when
+ * the startObserving() is called for the first time in that process. Once
+ * started the UEvent thread will not stop (although it can stop notifying
+ * UEventObserver's via stopObserving()).<p>
+ *
+ * @hide
+*/
+public abstract class UEventObserver {
+ private static final String TAG = UEventObserver.class.getSimpleName();
+
+ /**
+ * Representation of a UEvent.
+ */
+ static public class UEvent {
+ // collection of key=value pairs parsed from the uevent message
+ public HashMap<String,String> mMap = new HashMap<String,String>();
+
+ public UEvent(String message) {
+ int offset = 0;
+ int length = message.length();
+
+ while (offset < length) {
+ int equals = message.indexOf('=', offset);
+ int at = message.indexOf(0, offset);
+ if (at < 0) break;
+
+ if (equals > offset && equals < at) {
+ // key is before the equals sign, and value is after
+ mMap.put(message.substring(offset, equals),
+ message.substring(equals + 1, at));
+ }
+
+ offset = at + 1;
+ }
+ }
+
+ public String get(String key) {
+ return mMap.get(key);
+ }
+
+ public String get(String key, String defaultValue) {
+ String result = mMap.get(key);
+ return (result == null ? defaultValue : result);
+ }
+
+ public String toString() {
+ return mMap.toString();
+ }
+ }
+
+ private static UEventThread sThread;
+ private static boolean sThreadStarted = false;
+
+ private static class UEventThread extends Thread {
+ /** Many to many mapping of string match to observer.
+ * Multimap would be better, but not available in android, so use
+ * an ArrayList where even elements are the String match and odd
+ * elements the corresponding UEventObserver observer */
+ private ArrayList<Object> mObservers = new ArrayList<Object>();
+
+ UEventThread() {
+ super("UEventObserver");
+ }
+
+ public void run() {
+ native_setup();
+
+ byte[] buffer = new byte[1024];
+ int len;
+ while (true) {
+ len = next_event(buffer);
+ if (len > 0) {
+ String bufferStr = new String(buffer, 0, len); // easier to search a String
+ synchronized (mObservers) {
+ for (int i = 0; i < mObservers.size(); i += 2) {
+ if (bufferStr.indexOf((String)mObservers.get(i)) != -1) {
+ ((UEventObserver)mObservers.get(i+1))
+ .onUEvent(new UEvent(bufferStr));
+ }
+ }
+ }
+ }
+ }
+ }
+ public void addObserver(String match, UEventObserver observer) {
+ synchronized(mObservers) {
+ mObservers.add(match);
+ mObservers.add(observer);
+ }
+ }
+ /** Removes every key/value pair where value=observer from mObservers */
+ public void removeObserver(UEventObserver observer) {
+ synchronized(mObservers) {
+ boolean found = true;
+ while (found) {
+ found = false;
+ for (int i = 0; i < mObservers.size(); i += 2) {
+ if (mObservers.get(i+1) == observer) {
+ mObservers.remove(i+1);
+ mObservers.remove(i);
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static native void native_setup();
+ private static native int next_event(byte[] buffer);
+
+ private static final synchronized void ensureThreadStarted() {
+ if (sThreadStarted == false) {
+ sThread = new UEventThread();
+ sThread.start();
+ sThreadStarted = true;
+ }
+ }
+
+ /**
+ * Begin observation of UEvent's.<p>
+ * This method will cause the UEvent thread to start if this is the first
+ * invocation of startObserving in this process.<p>
+ * Once called, the UEvent thread will call onUEvent() when an incoming
+ * UEvent matches the specified string.<p>
+ * This method can be called multiple times to register multiple matches.
+ * Only one call to stopObserving is required even with multiple registered
+ * matches.
+ * @param match A substring of the UEvent to match. Use "" to match all
+ * UEvent's
+ */
+ public final synchronized void startObserving(String match) {
+ ensureThreadStarted();
+ sThread.addObserver(match, this);
+ }
+
+ /**
+ * End observation of UEvent's.<p>
+ * This process's UEvent thread will never call onUEvent() on this
+ * UEventObserver after this call. Repeated calls have no effect.
+ */
+ public final synchronized void stopObserving() {
+ sThread.removeObserver(this);
+ }
+
+ /**
+ * Subclasses of UEventObserver should override this method to handle
+ * UEvents.
+ */
+ public abstract void onUEvent(UEvent event);
+
+ protected void finalize() throws Throwable {
+ try {
+ stopObserving();
+ } finally {
+ super.finalize();
+ }
+ }
+}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
new file mode 100644
index 0000000..0f75289
--- /dev/null
+++ b/core/java/android/os/Vibrator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2006 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 android.os;
+
+/**
+ * Class that operates the vibrator on the device.
+ * <p>
+ * If your process exits, any vibration you started with will stop.
+ */
+public class Vibrator
+{
+ IHardwareService mService;
+
+ /** @hide */
+ public Vibrator()
+ {
+ mService = IHardwareService.Stub.asInterface(
+ ServiceManager.getService("hardware"));
+ }
+
+ /**
+ * Turn the vibrator on.
+ *
+ * @param milliseconds How long to vibrate for.
+ */
+ public void vibrate(long milliseconds)
+ {
+ try {
+ mService.vibrate(milliseconds);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Vibrate with a given pattern.
+ *
+ * <p>
+ * Pass in an array of ints that are the times at which to turn on or off
+ * the vibrator. The first one is how long to wait before turning it on,
+ * and then after that it alternates. If you want to repeat, pass the
+ * index into the pattern at which to start the repeat.
+ *
+ * @param pattern an array of longs of times to turn the vibrator on or off.
+ * @param repeat the index into pattern at which to repeat, or -1 if
+ * you don't want to repeat.
+ */
+ public void vibrate(long[] pattern, int repeat)
+ {
+ // catch this here because the server will do nothing. pattern may
+ // not be null, let that be checked, because the server will drop it
+ // anyway
+ if (repeat < pattern.length) {
+ try {
+ mService.vibratePattern(pattern, repeat, new Binder());
+ } catch (RemoteException e) {
+ }
+ } else {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Turn the vibrator off.
+ */
+ public void cancel()
+ {
+ try {
+ mService.cancelVibrate();
+ } catch (RemoteException e) {
+ }
+ }
+}
diff --git a/core/java/android/os/package.html b/core/java/android/os/package.html
new file mode 100644
index 0000000..fb0ecda
--- /dev/null
+++ b/core/java/android/os/package.html
@@ -0,0 +1,6 @@
+<HTML>
+<BODY>
+Provides basic operating system services, message passing, and inter-process
+communication on the device.
+</BODY>
+</HTML>
\ No newline at end of file