diff options
Diffstat (limited to 'core/java/android/os')
70 files changed, 14226 insertions, 0 deletions
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 |