summaryrefslogtreecommitdiffstats
path: root/core/java/android/app
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
commit9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch)
treed88beb88001f2482911e3d28e43833b50e4b4e97 /core/java/android/app
parentd83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff)
downloadframeworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip
frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz
frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/java/android/app')
-rw-r--r--core/java/android/app/Activity.java3598
-rw-r--r--core/java/android/app/ActivityGroup.java127
-rw-r--r--core/java/android/app/ActivityManager.java761
-rw-r--r--core/java/android/app/ActivityManagerNative.java2135
-rw-r--r--core/java/android/app/ActivityThread.java3916
-rw-r--r--core/java/android/app/AlarmManager.java276
-rw-r--r--core/java/android/app/AlertDialog.java795
-rw-r--r--core/java/android/app/AliasActivity.java123
-rw-r--r--core/java/android/app/Application.java74
-rw-r--r--core/java/android/app/ApplicationContext.java2765
-rw-r--r--core/java/android/app/ApplicationLoaders.java72
-rw-r--r--core/java/android/app/ApplicationThreadNative.java658
-rw-r--r--core/java/android/app/DatePickerDialog.java185
-rw-r--r--core/java/android/app/Dialog.java954
-rw-r--r--core/java/android/app/ExpandableListActivity.java324
-rw-r--r--core/java/android/app/IActivityManager.java368
-rw-r--r--core/java/android/app/IActivityPendingResult.aidl27
-rw-r--r--core/java/android/app/IActivityWatcher.aidl55
-rwxr-xr-xcore/java/android/app/IAlarmManager.aidl34
-rw-r--r--core/java/android/app/IApplicationThread.java118
-rw-r--r--core/java/android/app/IInstrumentationWatcher.aidl31
-rwxr-xr-xcore/java/android/app/IIntentReceiver.aidl33
-rw-r--r--core/java/android/app/IIntentSender.aidl27
-rw-r--r--core/java/android/app/INotificationManager.aidl34
-rw-r--r--core/java/android/app/ISearchManager.aidl25
-rw-r--r--core/java/android/app/IServiceConnection.aidl26
-rw-r--r--core/java/android/app/IStatusBar.aidl29
-rwxr-xr-xcore/java/android/app/IThumbnailReceiver.aidl30
-rw-r--r--core/java/android/app/ITransientNotification.aidl25
-rw-r--r--core/java/android/app/IWallpaperService.aidl55
-rw-r--r--core/java/android/app/IWallpaperServiceCallback.aidl31
-rw-r--r--core/java/android/app/Instrumentation.java1613
-rw-r--r--core/java/android/app/IntentService.java74
-rw-r--r--core/java/android/app/KeyguardManager.java155
-rw-r--r--core/java/android/app/LauncherActivity.java380
-rw-r--r--core/java/android/app/ListActivity.java316
-rw-r--r--core/java/android/app/LocalActivityManager.java627
-rw-r--r--core/java/android/app/Notification.aidl19
-rw-r--r--core/java/android/app/Notification.java483
-rw-r--r--core/java/android/app/NotificationManager.java134
-rw-r--r--core/java/android/app/PendingIntent.aidl20
-rw-r--r--core/java/android/app/PendingIntent.java508
-rw-r--r--core/java/android/app/ProgressDialog.java301
-rw-r--r--core/java/android/app/ResultInfo.java86
-rw-r--r--core/java/android/app/SearchDialog.java1593
-rw-r--r--core/java/android/app/SearchManager.java1385
-rw-r--r--core/java/android/app/Service.java378
-rw-r--r--core/java/android/app/StatusBarManager.java139
-rw-r--r--core/java/android/app/TabActivity.java148
-rw-r--r--core/java/android/app/TimePickerDialog.java162
-rw-r--r--core/java/android/app/package.html72
51 files changed, 26304 insertions, 0 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
new file mode 100644
index 0000000..849a37d
--- /dev/null
+++ b/core/java/android/app/Activity.java
@@ -0,0 +1,3598 @@
+/*
+ * 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.app;
+
+import android.content.ComponentCallbacks;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.text.method.TextKeyListener;
+import android.util.AttributeSet;
+import android.util.Config;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.ContextMenu;
+import android.view.ContextThemeWrapper;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewManager;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnCreateContextMenuListener;
+import android.widget.AdapterView;
+
+import com.android.internal.policy.PolicyManager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * An activity is a single, focused thing that the user can do. Almost all
+ * activities interact with the user, so the Activity class takes care of
+ * creating a window for you in which you can place your UI with
+ * {@link #setContentView}. While activities are often presented to the user
+ * as full-screen windows, they can also be used in other ways: as floating
+ * windows (via a theme with {@link android.R.attr#windowIsFloating} set)
+ * or embedded inside of another activity (using {@link ActivityGroup}).
+ *
+ * There are two methods almost all subclasses of Activity will implement:
+ *
+ * <ul>
+ * <li> {@link #onCreate} is where you initialize your activity. Most
+ * importantly, here you will usually call {@link #setContentView(int)}
+ * with a layout resource defining your UI, and using {@link #findViewById}
+ * to retrieve the widgets in that UI that you need to interact with
+ * programmatically.
+ *
+ * <li> {@link #onPause} is where you deal with the user leaving your
+ * activity. Most importantly, any changes made by the user should at this
+ * point be committed (usually to the
+ * {@link android.content.ContentProvider} holding the data).
+ * </ul>
+ *
+ * <p>To be of use with {@link android.content.Context#startActivity Context.startActivity()}, all
+ * activity classes must have a corresponding
+ * {@link android.R.styleable#AndroidManifestActivity &lt;activity&gt;}
+ * declaration in their package's <code>AndroidManifest.xml</code>.</p>
+ *
+ * <p>The Activity class is an important part of an application's overall lifecycle,
+ * and the way activities are launched and put together is a fundamental
+ * part of the platform's application model. For a detailed perspective on the structure of
+ * Android applications and lifecycles, please read the <em>Dev Guide</em> document on
+ * <a href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a>.</p>
+ *
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#ActivityLifecycle">Activity Lifecycle</a>
+ * <li><a href="#ConfigurationChanges">Configuration Changes</a>
+ * <li><a href="#StartingActivities">Starting Activities and Getting Results</a>
+ * <li><a href="#SavingPersistentState">Saving Persistent State</a>
+ * <li><a href="#Permissions">Permissions</a>
+ * <li><a href="#ProcessLifecycle">Process Lifecycle</a>
+ * </ol>
+ *
+ * <a name="ActivityLifecycle"></a>
+ * <h3>Activity Lifecycle</h3>
+ *
+ * <p>Activities in the system are managed as an <em>activity stack</em>.
+ * When a new activity is started, it is placed on the top of the stack
+ * and becomes the running activity -- the previous activity always remains
+ * below it in the stack, and will not come to the foreground again until
+ * the new activity exits.</p>
+ *
+ * <p>An activity has essentially four states:</p>
+ * <ul>
+ * <li> If an activity in the foreground of the screen (at the top of
+ * the stack),
+ * it is <em>active</em> or <em>running</em>. </li>
+ * <li>If an activity has lost focus but is still visible (that is, a new non-full-sized
+ * or transparent activity has focus on top of your activity), it
+ * is <em>paused</em>. A paused activity is completely alive (it
+ * maintains all state and member information and remains attached to
+ * the window manager), but can be killed by the system in extreme
+ * low memory situations.
+ * <li>If an activity is completely obscured by another activity,
+ * it is <em>stopped</em>. It still retains all state and member information,
+ * however, it is no longer visible to the user so its window is hidden
+ * and it will often be killed by the system when memory is needed
+ * elsewhere.</li>
+ * <li>If an activity is paused or stopped, the system can drop the activity
+ * from memory by either asking it to finish, or simply killing its
+ * process. When it is displayed again to the user, it must be
+ * completely restarted and restored to its previous state.</li>
+ * </ul>
+ *
+ * <p>The following diagram shows the important state paths of an Activity.
+ * The square rectangles represent callback methods you can implement to
+ * perform operations when the Activity moves between states. The colored
+ * ovals are major states the Activity can be in.</p>
+ *
+ * <p><img src="../../../images/activity_lifecycle.png"
+ * alt="State diagram for an Android Activity Lifecycle." border="0" /></p>
+ *
+ * <p>There are three key loops you may be interested in monitoring within your
+ * activity:
+ *
+ * <ul>
+ * <li>The <b>entire lifetime</b> of an activity happens between the first call
+ * to {@link android.app.Activity#onCreate} through to a single final call
+ * to {@link android.app.Activity#onDestroy}. An activity will do all setup
+ * of "global" state in onCreate(), and release all remaining resources in
+ * onDestroy(). For example, if it has a thread running in the background
+ * to download data from the network, it may create that thread in onCreate()
+ * and then stop the thread in onDestroy().
+ *
+ * <li>The <b>visible lifetime</b> of an activity happens between a call to
+ * {@link android.app.Activity#onStart} until a corresponding call to
+ * {@link android.app.Activity#onStop}. During this time the user can see the
+ * activity on-screen, though it may not be in the foreground and interacting
+ * with the user. Between these two methods you can maintain resources that
+ * are needed to show the activity to the user. For example, you can register
+ * a {@link android.content.BroadcastReceiver} in onStart() to monitor for changes
+ * that impact your UI, and unregister it in onStop() when the user an no
+ * longer see what you are displaying. The onStart() and onStop() methods
+ * can be called multiple times, as the activity becomes visible and hidden
+ * to the user.
+ *
+ * <li>The <b>foreground lifetime</b> of an activity happens between a call to
+ * {@link android.app.Activity#onResume} until a corresponding call to
+ * {@link android.app.Activity#onPause}. During this time the activity is
+ * in front of all other activities and interacting with the user. An activity
+ * can frequently go between the resumed and paused states -- for example when
+ * the device goes to sleep, when an activity result is delivered, when a new
+ * intent is delivered -- so the code in these methods should be fairly
+ * lightweight.
+ * </ul>
+ *
+ * <p>The entire lifecycle of an activity is defined by the following
+ * Activity methods. All of these are hooks that you can override
+ * to do appropriate work when the activity changes state. All
+ * activities will implement {@link android.app.Activity#onCreate}
+ * to do their initial setup; many will also implement
+ * {@link android.app.Activity#onPause} to commit changes to data and
+ * otherwise prepare to stop interacting with the user. You should always
+ * call up to your superclass when implementing these methods.</p>
+ *
+ * </p>
+ * <pre class="prettyprint">
+ * public class Activity extends ApplicationContext {
+ * protected void onCreate(Bundle savedInstanceState);
+ *
+ * protected void onStart();
+ *
+ * protected void onRestart();
+ *
+ * protected void onResume();
+ *
+ * protected void onPause();
+ *
+ * protected void onStop();
+ *
+ * protected void onDestroy();
+ * }
+ * </pre>
+ *
+ * <p>In general the movement through an activity's lifecycle looks like
+ * this:</p>
+ *
+ * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
+ * <colgroup align="left" span="3" />
+ * <colgroup align="left" />
+ * <colgroup align="center" />
+ * <colgroup align="center" />
+ *
+ * <thead>
+ * <tr><th colspan="3">Method</th> <th>Description</th> <th>Killable?</th> <th>Next</th></tr>
+ * </thead>
+ *
+ * <tbody>
+ * <tr><th colspan="3" align="left" border="0">{@link android.app.Activity#onCreate onCreate()}</th>
+ * <td>Called when the activity is first created.
+ * This is where you should do all of your normal static set up:
+ * create views, bind data to lists, etc. This method also
+ * provides you with a Bundle containing the activity's previously
+ * frozen state, if there was one.
+ * <p>Always followed by <code>onStart()</code>.</td>
+ * <td align="center">No</td>
+ * <td align="center"><code>onStart()</code></td>
+ * </tr>
+ *
+ * <tr><td rowspan="5" style="border-left: none; border-right: none;">&nbsp;&nbsp;&nbsp;&nbsp;</td>
+ * <th colspan="2" align="left" border="0">{@link android.app.Activity#onRestart onRestart()}</th>
+ * <td>Called after your activity has been stopped, prior to it being
+ * started again.
+ * <p>Always followed by <code>onStart()</code></td>
+ * <td align="center">No</td>
+ * <td align="center"><code>onStart()</code></td>
+ * </tr>
+ *
+ * <tr><th colspan="2" align="left" border="0">{@link android.app.Activity#onStart onStart()}</th>
+ * <td>Called when the activity is becoming visible to the user.
+ * <p>Followed by <code>onResume()</code> if the activity comes
+ * to the foreground, or <code>onStop()</code> if it becomes hidden.</td>
+ * <td align="center">No</td>
+ * <td align="center"><code>onResume()</code> or <code>onStop()</code></td>
+ * </tr>
+ *
+ * <tr><td rowspan="2" style="border-left: none;">&nbsp;&nbsp;&nbsp;&nbsp;</td>
+ * <th align="left" border="0">{@link android.app.Activity#onResume onResume()}</th>
+ * <td>Called when the activity will start
+ * interacting with the user. At this point your activity is at
+ * the top of the activity stack, with user input going to it.
+ * <p>Always followed by <code>onPause()</code>.</td>
+ * <td align="center">No</td>
+ * <td align="center"><code>onPause()</code></td>
+ * </tr>
+ *
+ * <tr><th align="left" border="0">{@link android.app.Activity#onPause onPause()}</th>
+ * <td>Called when the system is about to start resuming a previous
+ * activity. This is typically used to commit unsaved changes to
+ * persistent data, stop animations and other things that may be consuming
+ * CPU, etc. Implementations of this method must be very quick because
+ * the next activity will not be resumed until this method returns.
+ * <p>Followed by either <code>onResume()</code> if the activity
+ * returns back to the front, or <code>onStop()</code> if it becomes
+ * invisible to the user.</td>
+ * <td align="center"><font color="#800000"><strong>Yes</strong></font></td>
+ * <td align="center"><code>onResume()</code> or<br>
+ * <code>onStop()</code></td>
+ * </tr>
+ *
+ * <tr><th colspan="2" align="left" border="0">{@link android.app.Activity#onStop onStop()}</th>
+ * <td>Called when the activity is no longer visible to the user, because
+ * another activity has been resumed and is covering this one. This
+ * may happen either because a new activity is being started, an existing
+ * one is being brought in front of this one, or this one is being
+ * destroyed.
+ * <p>Followed by either <code>onRestart()</code> if
+ * this activity is coming back to interact with the user, or
+ * <code>onDestroy()</code> if this activity is going away.</td>
+ * <td align="center"><font color="#800000"><strong>Yes</strong></font></td>
+ * <td align="center"><code>onRestart()</code> or<br>
+ * <code>onDestroy()</code></td>
+ * </tr>
+ *
+ * <tr><th colspan="3" align="left" border="0">{@link android.app.Activity#onDestroy onDestroy()}</th>
+ * <td>The final call you receive before your
+ * activity is destroyed. This can happen either because the
+ * activity is finishing (someone called {@link Activity#finish} on
+ * it, or because the system is temporarily destroying this
+ * instance of the activity to save space. You can distinguish
+ * between these two scenarios with the {@link
+ * Activity#isFinishing} method.</td>
+ * <td align="center"><font color="#800000"><strong>Yes</strong></font></td>
+ * <td align="center"><em>nothing</em></td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <p>Note the "Killable" column in the above table -- for those methods that
+ * are marked as being killable, after that method returns the process hosting the
+ * activity may killed by the system <em>at any time</em> without another line
+ * of its code being executed. Because of this, you should use the
+ * {@link #onPause} method to write any persistent data (such as user edits)
+ * to storage. In addition, the method
+ * {@link #onSaveInstanceState(Bundle)} is called before placing the activity
+ * in such a background state, allowing you to save away any dynamic instance
+ * state in your activity into the given Bundle, to be later received in
+ * {@link #onCreate} if the activity needs to be re-created.
+ * See the <a href="#ProcessLifecycle">Process Lifecycle</a>
+ * section for more information on how the lifecycle of a process is tied
+ * to the activities it is hosting. Note that it is important to save
+ * persistent data in {@link #onPause} instead of {@link #onSaveInstanceState}
+ * because the later is not part of the lifecycle callbacks, so will not
+ * be called in every situation as described in its documentation.</p>
+ *
+ * <p>For those methods that are not marked as being killable, the activity's
+ * process will not be killed by the system starting from the time the method
+ * is called and continuing after it returns. Thus an activity is in the killable
+ * state, for example, between after <code>onPause()</code> to the start of
+ * <code>onResume()</code>.</p>
+ *
+ * <a name="ConfigurationChanges"></a>
+ * <h3>Configuration Changes</h3>
+ *
+ * <p>If the configuration of the device (as defined by the
+ * {@link Configuration Resources.Configuration} class) changes,
+ * then anything displaying a user interface will need to update to match that
+ * configuration. Because Activity is the primary mechanism for interacting
+ * with the user, it includes special support for handling configuration
+ * changes.</p>
+ *
+ * <p>Unless you specify otherwise, a configuration change (such as a change
+ * in screen orientation, language, input devices, etc) will cause your
+ * current activity to be <em>destroyed</em>, going through the normal activity
+ * lifecycle process of {@link #onPause},
+ * {@link #onStop}, and {@link #onDestroy} as appropriate. If the activity
+ * had been in the foreground or visible to the user, once {@link #onDestroy} is
+ * called in that instance then a new instance of the activity will be
+ * created, with whatever savedInstanceState the previous instance had generated
+ * from {@link #onSaveInstanceState}.</p>
+ *
+ * <p>This is done because any application resource,
+ * including layout files, can change based on any configuration value. Thus
+ * the only safe way to handle a configuration change is to re-retrieve all
+ * resources, including layouts, drawables, and strings. Because activities
+ * must already know how to save their state and re-create themselves from
+ * that state, this is a convenient way to have an activity restart itself
+ * with a new configuration.</p>
+ *
+ * <p>In some special cases, you may want to bypass restarting of your
+ * activity based on one or more types of configuration changes. This is
+ * done with the {@link android.R.attr#configChanges android:configChanges}
+ * attribute in its manifest. For any types of configuration changes you say
+ * that you handle there, you will receive a call to your current activity's
+ * {@link #onConfigurationChanged} method instead of being restarted. If
+ * a configuration change involves any that you do not handle, however, the
+ * activity will still be restarted and {@link #onConfigurationChanged}
+ * will not be called.</p>
+ *
+ * <a name="StartingActivities"></a>
+ * <h3>Starting Activities and Getting Results</h3>
+ *
+ * <p>The {@link android.app.Activity#startActivity}
+ * method is used to start a
+ * new activity, which will be placed at the top of the activity stack. It
+ * takes a single argument, an {@link android.content.Intent Intent},
+ * which describes the activity
+ * to be executed.</p>
+ *
+ * <p>Sometimes you want to get a result back from an activity when it
+ * ends. For example, you may start an activity that lets the user pick
+ * a person in a list of contacts; when it ends, it returns the person
+ * that was selected. To do this, you call the
+ * {@link android.app.Activity#startActivityForResult(Intent, int)}
+ * version with a second integer parameter identifying the call. The result
+ * will come back through your {@link android.app.Activity#onActivityResult}
+ * method.</p>
+ *
+ * <p>When an activity exits, it can call
+ * {@link android.app.Activity#setResult(int)}
+ * to return data back to its parent. It must always supply a result code,
+ * which can be the standard results RESULT_CANCELED, RESULT_OK, or any
+ * custom values starting at RESULT_FIRST_USER. In addition, it can optionally
+ * return back an Intent containing any additional data it wants. All of this
+ * information appears back on the
+ * parent's <code>Activity.onActivityResult()</code>, along with the integer
+ * identifier it originally supplied.</p>
+ *
+ * <p>If a child activity fails for any reason (such as crashing), the parent
+ * activity will receive a result with the code RESULT_CANCELED.</p>
+ *
+ * <pre class="prettyprint">
+ * public class MyActivity extends Activity {
+ * ...
+ *
+ * static final int PICK_CONTACT_REQUEST = 0;
+ *
+ * protected boolean onKeyDown(int keyCode, KeyEvent event) {
+ * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ * // When the user center presses, let them pick a contact.
+ * startActivityForResult(
+ * new Intent(Intent.ACTION_PICK,
+ * new Uri("content://contacts")),
+ * PICK_CONTACT_REQUEST);
+ * return true;
+ * }
+ * return false;
+ * }
+ *
+ * protected void onActivityResult(int requestCode, int resultCode,
+ * Intent data) {
+ * if (requestCode == PICK_CONTACT_REQUEST) {
+ * if (resultCode == RESULT_OK) {
+ * // A contact was picked. Here we will just display it
+ * // to the user.
+ * startActivity(new Intent(Intent.ACTION_VIEW, data));
+ * }
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * <a name="SavingPersistentState"></a>
+ * <h3>Saving Persistent State</h3>
+ *
+ * <p>There are generally two kinds of persistent state than an activity
+ * will deal with: shared document-like data (typically stored in a SQLite
+ * database using a {@linkplain android.content.ContentProvider content provider})
+ * and internal state such as user preferences.</p>
+ *
+ * <p>For content provider data, we suggest that activities use a
+ * "edit in place" user model. That is, any edits a user makes are effectively
+ * made immediately without requiring an additional confirmation step.
+ * Supporting this model is generally a simple matter of following two rules:</p>
+ *
+ * <ul>
+ * <li> <p>When creating a new document, the backing database entry or file for
+ * it is created immediately. For example, if the user chooses to write
+ * a new e-mail, a new entry for that e-mail is created as soon as they
+ * start entering data, so that if they go to any other activity after
+ * that point this e-mail will now appear in the list of drafts.</p>
+ * <li> <p>When an activity's <code>onPause()</code> method is called, it should
+ * commit to the backing content provider or file any changes the user
+ * has made. This ensures that those changes will be seen by any other
+ * activity that is about to run. You will probably want to commit
+ * your data even more aggressively at key times during your
+ * activity's lifecycle: for example before starting a new
+ * activity, before finishing your own activity, when the user
+ * switches between input fields, etc.</p>
+ * </ul>
+ *
+ * <p>This model is designed to prevent data loss when a user is navigating
+ * between activities, and allows the system to safely kill an activity (because
+ * system resources are needed somewhere else) at any time after it has been
+ * paused. Note this implies
+ * that the user pressing BACK from your activity does <em>not</em>
+ * mean "cancel" -- it means to leave the activity with its current contents
+ * saved away. Cancelling edits in an activity must be provided through
+ * some other mechanism, such as an explicit "revert" or "undo" option.</p>
+ *
+ * <p>See the {@linkplain android.content.ContentProvider content package} for
+ * more information about content providers. These are a key aspect of how
+ * different activities invoke and propagate data between themselves.</p>
+ *
+ * <p>The Activity class also provides an API for managing internal persistent state
+ * associated with an activity. This can be used, for example, to remember
+ * the user's preferred initial display in a calendar (day view or week view)
+ * or the user's default home page in a web browser.</p>
+ *
+ * <p>Activity persistent state is managed
+ * with the method {@link #getPreferences},
+ * allowing you to retrieve and
+ * modify a set of name/value pairs associated with the activity. To use
+ * preferences that are shared across multiple application components
+ * (activities, receivers, services, providers), you can use the underlying
+ * {@link Context#getSharedPreferences Context.getSharedPreferences()} method
+ * to retrieve a preferences
+ * object stored under a specific name.
+ * (Note that it is not possible to share settings data across application
+ * packages -- for that you will need a content provider.)</p>
+ *
+ * <p>Here is an excerpt from a calendar activity that stores the user's
+ * preferred view mode in its persistent settings:</p>
+ *
+ * <pre class="prettyprint">
+ * public class CalendarActivity extends Activity {
+ * ...
+ *
+ * static final int DAY_VIEW_MODE = 0;
+ * static final int WEEK_VIEW_MODE = 1;
+ *
+ * private SharedPreferences mPrefs;
+ * private int mCurViewMode;
+ *
+ * protected void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ *
+ * SharedPreferences mPrefs = getSharedPreferences();
+ * mCurViewMode = mPrefs.getInt("view_mode" DAY_VIEW_MODE);
+ * }
+ *
+ * protected void onPause() {
+ * super.onPause();
+ *
+ * SharedPreferences.Editor ed = mPrefs.edit();
+ * ed.putInt("view_mode", mCurViewMode);
+ * ed.commit();
+ * }
+ * }
+ * </pre>
+ *
+ * <a name="Permissions"></a>
+ * <h3>Permissions</h3>
+ *
+ * <p>The ability to start a particular Activity can be enforced when it is
+ * declared in its
+ * manifest's {@link android.R.styleable#AndroidManifestActivity &lt;activity&gt;}
+ * tag. By doing so, other applications will need to declare a corresponding
+ * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
+ * element in their own manifest to be able to start that activity.
+ *
+ * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
+ * document for more information on permissions and security in general.
+ *
+ * <a name="ProcessLifecycle"></a>
+ * <h3>Process Lifecycle</h3>
+ *
+ * <p>The Android system attempts to keep application process around for as
+ * long as possible, but eventually will need to remove old processes when
+ * memory runs low. As described in <a href="#ActivityLifecycle">Activity
+ * Lifecycle</a>, the decision about which process to remove is intimately
+ * tied to the state of the user's interaction with it. In general, there
+ * are four states a process can be in based on the activities running in it,
+ * listed here in order of importance. The system will kill less important
+ * processes (the last ones) before it resorts to killing more important
+ * processes (the first ones).
+ *
+ * <ol>
+ * <li> <p>The <b>foreground activity</b> (the activity at the top of the screen
+ * that the user is currently interacting with) is considered the most important.
+ * Its process will only be killed as a last resort, if it uses more memory
+ * than is available on the device. Generally at this point the device has
+ * reached a memory paging state, so this is required in order to keep the user
+ * interface responsive.
+ * <li> <p>A <b>visible activity</b> (an activity that is visible to the user
+ * but not in the foreground, such as one sitting behind a foreground dialog)
+ * is considered extremely important and will not be killed unless that is
+ * required to keep the foreground activity running.
+ * <li> <p>A <b>background activity</b> (an activity that is not visible to
+ * the user and has been paused) is no longer critical, so the system may
+ * safely kill its process to reclaim memory for other foreground or
+ * visible processes. If its process needs to be killed, when the user navigates
+ * back to the activity (making it visible on the screen again), its
+ * {@link #onCreate} method will be called with the savedInstanceState it had previously
+ * supplied in {@link #onSaveInstanceState} so that it can restart itself in the same
+ * state as the user last left it.
+ * <li> <p>An <b>empty process</b> is one hosting no activities or other
+ * application components (such as {@link Service} or
+ * {@link android.content.BroadcastReceiver} classes). These are killed very
+ * quickly by the system as memory becomes low. For this reason, any
+ * background operation you do outside of an activity must be executed in the
+ * context of an activity BroadcastReceiver or Service to ensure that the system
+ * knows it needs to keep your process around.
+ * </ol>
+ *
+ * <p>Sometimes an Activity may need to do a long-running operation that exists
+ * independently of the activity lifecycle itself. An example may be a camera
+ * application that allows you to upload a picture to a web site. The upload
+ * may take a long time, and the application should allow the user to leave
+ * the application will it is executing. To accomplish this, your Activity
+ * should start a {@link Service} in which the upload takes place. This allows
+ * the system to properly prioritize your process (considering it to be more
+ * important than other non-visible applications) for the duration of the
+ * upload, independent of whether the original activity is paused, stopped,
+ * or finished.
+ */
+public class Activity extends ContextThemeWrapper
+ implements LayoutInflater.Factory,
+ Window.Callback, KeyEvent.Callback,
+ OnCreateContextMenuListener, ComponentCallbacks {
+ private static final String TAG = "Activity";
+
+ /** Standard activity result: operation canceled. */
+ public static final int RESULT_CANCELED = 0;
+ /** Standard activity result: operation succeeded. */
+ public static final int RESULT_OK = -1;
+ /** Start of user-defined activity results. */
+ public static final int RESULT_FIRST_USER = 1;
+
+ private static long sInstanceCount = 0;
+
+ private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
+ private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
+ private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
+ private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
+ private static final String SAVED_SEARCH_DIALOG_KEY = "android:search_dialog";
+
+ private SparseArray<Dialog> mManagedDialogs;
+
+ // set by the thread after the constructor and before onCreate(Bundle savedInstanceState) is called.
+ private Instrumentation mInstrumentation;
+ private IBinder mToken;
+ /*package*/ String mEmbeddedID;
+ private Application mApplication;
+ private Intent mIntent;
+ private ComponentName mComponent;
+ /*package*/ ActivityInfo mActivityInfo;
+ /*package*/ ActivityThread mMainThread;
+ /*package*/ Object mLastNonConfigurationInstance;
+ /*package*/ HashMap<String,Object> mLastNonConfigurationChildInstances;
+ Activity mParent;
+ boolean mCalled;
+ private boolean mResumed;
+ private boolean mStopped;
+ boolean mFinished;
+ boolean mStartedActivity;
+ /*package*/ int mConfigChangeFlags;
+ /*package*/ Configuration mCurrentConfig;
+
+ private Window mWindow;
+
+ private WindowManager mWindowManager;
+ /*package*/ View mDecor = null;
+ /*package*/ boolean mWindowAdded = false;
+ /*package*/ boolean mVisibleFromServer = false;
+ /*package*/ boolean mVisibleFromClient = true;
+
+ private CharSequence mTitle;
+ private int mTitleColor = 0;
+
+ private static final class ManagedCursor {
+ ManagedCursor(Cursor cursor) {
+ mCursor = cursor;
+ mReleased = false;
+ mUpdated = false;
+ }
+
+ private final Cursor mCursor;
+ private boolean mReleased;
+ private boolean mUpdated;
+ }
+ private final ArrayList<ManagedCursor> mManagedCursors =
+ new ArrayList<ManagedCursor>();
+
+ // protected by synchronized (this)
+ int mResultCode = RESULT_CANCELED;
+ Intent mResultData = null;
+
+ private boolean mTitleReady = false;
+
+ private int mDefaultKeyMode = DEFAULT_KEYS_DISABLE;
+ private SpannableStringBuilder mDefaultKeySsb = null;
+
+ protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused};
+
+ private Thread mUiThread;
+ private final Handler mHandler = new Handler();
+
+ public Activity() {
+ ++sInstanceCount;
+ }
+
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ --sInstanceCount;
+ }
+
+ public static long getInstanceCount() {
+ return sInstanceCount;
+ }
+
+ /** Return the intent that started this activity. */
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ /**
+ * Change the intent returned by {@link #getIntent}. This holds a
+ * reference to the given intent; it does not copy it. Often used in
+ * conjunction with {@link #onNewIntent}.
+ *
+ * @param newIntent The new Intent object to return from getIntent
+ *
+ * @see #getIntent
+ * @see #onNewIntent
+ */
+ public void setIntent(Intent newIntent) {
+ mIntent = newIntent;
+ }
+
+ /** Return the application that owns this activity. */
+ public final Application getApplication() {
+ return mApplication;
+ }
+
+ /** Is this activity embedded inside of another activity? */
+ public final boolean isChild() {
+ return mParent != null;
+ }
+
+ /** Return the parent activity if this view is an embedded child. */
+ public final Activity getParent() {
+ return mParent;
+ }
+
+ /** Retrieve the window manager for showing custom windows. */
+ public WindowManager getWindowManager() {
+ return mWindowManager;
+ }
+
+ /**
+ * Retrieve the current {@link android.view.Window} for the activity.
+ * This can be used to directly access parts of the Window API that
+ * are not available through Activity/Screen.
+ *
+ * @return Window The current window, or null if the activity is not
+ * visual.
+ */
+ public Window getWindow() {
+ return mWindow;
+ }
+
+ /**
+ * Calls {@link android.view.Window#getCurrentFocus} on the
+ * Window of this Activity to return the currently focused view.
+ *
+ * @return View The current View with focus or null.
+ *
+ * @see #getWindow
+ * @see android.view.Window#getCurrentFocus
+ */
+ public View getCurrentFocus() {
+ return mWindow != null ? mWindow.getCurrentFocus() : null;
+ }
+
+ @Override
+ public int getWallpaperDesiredMinimumWidth() {
+ int width = super.getWallpaperDesiredMinimumWidth();
+ return width <= 0 ? getWindowManager().getDefaultDisplay().getWidth() : width;
+ }
+
+ @Override
+ public int getWallpaperDesiredMinimumHeight() {
+ int height = super.getWallpaperDesiredMinimumHeight();
+ return height <= 0 ? getWindowManager().getDefaultDisplay().getHeight() : height;
+ }
+
+ /**
+ * Called when the activity is starting. This is where most initialization
+ * should go: calling {@link #setContentView(int)} to inflate the
+ * activity's UI, using {@link #findViewById} to programmatically interact
+ * with widgets in the UI, calling
+ * {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve
+ * cursors for data being displayed, etc.
+ *
+ * <p>You can call {@link #finish} from within this function, in
+ * which case onDestroy() will be immediately called without any of the rest
+ * of the activity lifecycle ({@link #onStart}, {@link #onResume},
+ * {@link #onPause}, etc) executing.
+ *
+ * <p><em>Derived classes must call through to the super class's
+ * implementation of this method. If they do not, an exception will be
+ * thrown.</em></p>
+ *
+ * @param savedInstanceState If the activity is being re-initialized after
+ * previously being shut down then this Bundle contains the data it most
+ * recently supplied in {@link #onSaveInstanceState}. <b><i>Note: Otherwise it is null.</i></b>
+ *
+ * @see #onStart
+ * @see #onSaveInstanceState
+ * @see #onRestoreInstanceState
+ * @see #onPostCreate
+ */
+ protected void onCreate(Bundle savedInstanceState) {
+ mVisibleFromClient = mWindow.getWindowStyle().getBoolean(
+ com.android.internal.R.styleable.Window_windowNoDisplay, true);
+ mCalled = true;
+ }
+
+ /**
+ * The hook for {@link ActivityThread} to restore the state of this activity.
+ *
+ * Calls {@link #onSaveInstanceState(android.os.Bundle)} and
+ * {@link #restoreManagedDialogs(android.os.Bundle)}.
+ *
+ * @param savedInstanceState contains the saved state
+ */
+ final void performRestoreInstanceState(Bundle savedInstanceState) {
+ onRestoreInstanceState(savedInstanceState);
+ restoreManagedDialogs(savedInstanceState);
+
+ // Also restore the state of a search dialog (if any)
+ // TODO more generic than just this manager
+ SearchManager searchManager =
+ (SearchManager) getSystemService(Context.SEARCH_SERVICE);
+ searchManager.restoreSearchDialog(savedInstanceState, SAVED_SEARCH_DIALOG_KEY);
+ }
+
+ /**
+ * This method is called after {@link #onStart} when the activity is
+ * being re-initialized from a previously saved state, given here in
+ * <var>state</var>. Most implementations will simply use {@link #onCreate}
+ * to restore their state, but it is sometimes convenient to do it here
+ * after all of the initialization has been done or to allow subclasses to
+ * decide whether to use your default implementation. The default
+ * implementation of this method performs a restore of any view state that
+ * had previously been frozen by {@link #onSaveInstanceState}.
+ *
+ * <p>This method is called between {@link #onStart} and
+ * {@link #onPostCreate}.
+ *
+ * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
+ *
+ * @see #onCreate
+ * @see #onPostCreate
+ * @see #onResume
+ * @see #onSaveInstanceState
+ */
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ if (mWindow != null) {
+ Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
+ if (windowState != null) {
+ mWindow.restoreHierarchyState(windowState);
+ }
+ }
+ }
+
+ /**
+ * Restore the state of any saved managed dialogs.
+ *
+ * @param savedInstanceState The bundle to restore from.
+ */
+ private void restoreManagedDialogs(Bundle savedInstanceState) {
+ final Bundle b = savedInstanceState.getBundle(SAVED_DIALOGS_TAG);
+ if (b == null) {
+ return;
+ }
+
+ final int[] ids = b.getIntArray(SAVED_DIALOG_IDS_KEY);
+ final int numDialogs = ids.length;
+ mManagedDialogs = new SparseArray<Dialog>(numDialogs);
+ for (int i = 0; i < numDialogs; i++) {
+ final Integer dialogId = ids[i];
+ Bundle dialogState = b.getBundle(savedDialogKeyFor(dialogId));
+ if (dialogState != null) {
+ final Dialog dialog = onCreateDialog(dialogId);
+ dialog.onRestoreInstanceState(dialogState);
+ mManagedDialogs.put(dialogId, dialog);
+ }
+ }
+ }
+
+ private String savedDialogKeyFor(int key) {
+ return SAVED_DIALOG_KEY_PREFIX + key;
+ }
+
+
+ /**
+ * Called when activity start-up is complete (after {@link #onStart}
+ * and {@link #onRestoreInstanceState} have been called). Applications will
+ * generally not implement this method; it is intended for system
+ * classes to do final initialization after application code has run.
+ *
+ * <p><em>Derived classes must call through to the super class's
+ * implementation of this method. If they do not, an exception will be
+ * thrown.</em></p>
+ *
+ * @param savedInstanceState If the activity is being re-initialized after
+ * previously being shut down then this Bundle contains the data it most
+ * recently supplied in {@link #onSaveInstanceState}. <b><i>Note: Otherwise it is null.</i></b>
+ * @see #onCreate
+ */
+ protected void onPostCreate(Bundle savedInstanceState) {
+ if (!isChild()) {
+ mTitleReady = true;
+ onTitleChanged(getTitle(), getTitleColor());
+ }
+ mCalled = true;
+ }
+
+ /**
+ * Called after {@link #onCreate} &mdash; or after {@link #onRestart} when
+ * the activity had been stopped, but is now again being displayed to the
+ * user. It will be followed by {@link #onResume}.
+ *
+ * <p><em>Derived classes must call through to the super class's
+ * implementation of this method. If they do not, an exception will be
+ * thrown.</em></p>
+ *
+ * @see #onCreate
+ * @see #onStop
+ * @see #onResume
+ */
+ protected void onStart() {
+ mCalled = true;
+ }
+
+ /**
+ * Called after {@link #onStop} when the current activity is being
+ * re-displayed to the user (the user has navigated back to it). It will
+ * be followed by {@link #onStart} and then {@link #onResume}.
+ *
+ * <p>For activities that are using raw {@link Cursor} objects (instead of
+ * creating them through
+ * {@link #managedQuery(android.net.Uri , String[], String, String[], String)},
+ * this is usually the place
+ * where the cursor should be requeried (because you had deactivated it in
+ * {@link #onStop}.
+ *
+ * <p><em>Derived classes must call through to the super class's
+ * implementation of this method. If they do not, an exception will be
+ * thrown.</em></p>
+ *
+ * @see #onStop
+ * @see #onStart
+ * @see #onResume
+ */
+ protected void onRestart() {
+ mCalled = true;
+ }
+
+ /**
+ * Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or
+ * {@link #onPause}, for your activity to start interacting with the user.
+ * This is a good place to begin animations, open exclusive-access devices
+ * (such as the camera), etc.
+ *
+ * <p>Keep in mind that onResume is not the best indicator that your activity
+ * is visible to the user; a system window such as the keyguard may be in
+ * front. Use {@link #onWindowFocusChanged} to know for certain that your
+ * activity is visible to the user (for example, to resume a game).
+ *
+ * <p><em>Derived classes must call through to the super class's
+ * implementation of this method. If they do not, an exception will be
+ * thrown.</em></p>
+ *
+ * @see #onRestoreInstanceState
+ * @see #onRestart
+ * @see #onPostResume
+ * @see #onPause
+ */
+ protected void onResume() {
+ mCalled = true;
+ }
+
+ /**
+ * Called when activity resume is complete (after {@link #onResume} has
+ * been called). Applications will generally not implement this method;
+ * it is intended for system classes to do final setup after application
+ * resume code has run.
+ *
+ * <p><em>Derived classes must call through to the super class's
+ * implementation of this method. If they do not, an exception will be
+ * thrown.</em></p>
+ *
+ * @see #onResume
+ */
+ protected void onPostResume() {
+ final Window win = getWindow();
+ if (win != null) win.makeActive();
+ mCalled = true;
+ }
+
+ /**
+ * This is called for activities that set launchMode to "singleTop" in
+ * their package, or if a client used the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP}
+ * flag when calling {@link #startActivity}. In either case, when the
+ * activity is re-launched while at the top of the activity stack instead
+ * of a new instance of the activity being started, onNewIntent() will be
+ * called on the existing instance with the Intent that was used to
+ * re-launch it.
+ *
+ * <p>An activity will always be paused before receiving a new intent, so
+ * you can count on {@link #onResume} being called after this method.
+ *
+ * <p>Note that {@link #getIntent} still returns the original Intent. You
+ * can use {@link #setIntent} to update it to this new Intent.
+ *
+ * @param intent The new intent that was started for the activity.
+ *
+ * @see #getIntent
+ * @see #setIntent
+ * @see #onResume
+ */
+ protected void onNewIntent(Intent intent) {
+ }
+
+ /**
+ * The hook for {@link ActivityThread} to save the state of this activity.
+ *
+ * Calls {@link #onSaveInstanceState(android.os.Bundle)}
+ * and {@link #saveManagedDialogs(android.os.Bundle)}.
+ *
+ * @param outState The bundle to save the state to.
+ */
+ final void performSaveInstanceState(Bundle outState) {
+ onSaveInstanceState(outState);
+ saveManagedDialogs(outState);
+
+ // Also save the state of a search dialog (if any)
+ // TODO more generic than just this manager
+ SearchManager searchManager =
+ (SearchManager) getSystemService(Context.SEARCH_SERVICE);
+ searchManager.saveSearchDialog(outState, SAVED_SEARCH_DIALOG_KEY);
+ }
+
+ /**
+ * Called to retrieve per-instance state from an activity before being killed
+ * so that the state can be restored in {@link #onCreate} or
+ * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
+ * will be passed to both).
+ *
+ * <p>This method is called before an activity may be killed so that when it
+ * comes back some time in the future it can restore its state. For example,
+ * if activity B is launched in front of activity A, and at some point activity
+ * A is killed to reclaim resources, activity A will have a chance to save the
+ * current state of its user interface via this method so that when the user
+ * returns to activity A, the state of the user interface can be restored
+ * via {@link #onCreate} or {@link #onRestoreInstanceState}.
+ *
+ * <p>Do not confuse this method with activity lifecycle callbacks such as
+ * {@link #onPause}, which is always called when an activity is being placed
+ * in the background or on its way to destruction, or {@link #onStop} which
+ * is called before destruction. One example of when {@link #onPause} and
+ * {@link #onStop} is called and not this method is when a user navigates back
+ * from activity B to activity A: there is no need to call {@link #onSaveInstanceState}
+ * on B because that particular instance will never be restored, so the
+ * system avoids calling it. An example when {@link #onPause} is called and
+ * not {@link #onSaveInstanceState} is when activity B is launched in front of activity A:
+ * the system may avoid calling {@link #onSaveInstanceState} on activity A if it isn't
+ * killed during the lifetime of B since the state of the user interface of
+ * A will stay intact.
+ *
+ * <p>The default implementation takes care of most of the UI per-instance
+ * state for you by calling {@link android.view.View#onSaveInstanceState()} on each
+ * view in the hierarchy that has an id, and by saving the id of the currently
+ * focused view (all of which is restored by the default implementation of
+ * {@link #onRestoreInstanceState}). If you override this method to save additional
+ * information not captured by each individual view, you will likely want to
+ * call through to the default implementation, otherwise be prepared to save
+ * all of the state of each view yourself.
+ *
+ * <p>If called, this method will occur before {@link #onStop}. There are
+ * no guarantees about whether it will occur before or after {@link #onPause}.
+ *
+ * @param outState Bundle in which to place your saved state.
+ *
+ * @see #onCreate
+ * @see #onRestoreInstanceState
+ * @see #onPause
+ */
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
+ }
+
+ /**
+ * Save the state of any managed dialogs.
+ *
+ * @param outState place to store the saved state.
+ */
+ private void saveManagedDialogs(Bundle outState) {
+ if (mManagedDialogs == null) {
+ return;
+ }
+
+ final int numDialogs = mManagedDialogs.size();
+ if (numDialogs == 0) {
+ return;
+ }
+
+ Bundle dialogState = new Bundle();
+
+ int[] ids = new int[mManagedDialogs.size()];
+
+ // save each dialog's bundle, gather the ids
+ for (int i = 0; i < numDialogs; i++) {
+ final int key = mManagedDialogs.keyAt(i);
+ ids[i] = key;
+ final Dialog dialog = mManagedDialogs.valueAt(i);
+ dialogState.putBundle(savedDialogKeyFor(key), dialog.onSaveInstanceState());
+ }
+
+ dialogState.putIntArray(SAVED_DIALOG_IDS_KEY, ids);
+ outState.putBundle(SAVED_DIALOGS_TAG, dialogState);
+ }
+
+
+ /**
+ * Called as part of the activity lifecycle when an activity is going into
+ * the background, but has not (yet) been killed. The counterpart to
+ * {@link #onResume}.
+ *
+ * <p>When activity B is launched in front of activity A, this callback will
+ * be invoked on A. B will not be created until A's {@link #onPause} returns,
+ * so be sure to not do anything lengthy here.
+ *
+ * <p>This callback is mostly used for saving any persistent state the
+ * activity is editing, to present a "edit in place" model to the user and
+ * making sure nothing is lost if there are not enough resources to start
+ * the new activity without first killing this one. This is also a good
+ * place to do things like stop animations and other things that consume a
+ * noticeable mount of CPU in order to make the switch to the next activity
+ * as fast as possible, or to close resources that are exclusive access
+ * such as the camera.
+ *
+ * <p>In situations where the system needs more memory it may kill paused
+ * processes to reclaim resources. Because of this, you should be sure
+ * that all of your state is saved by the time you return from
+ * this function. In general {@link #onSaveInstanceState} is used to save
+ * per-instance state in the activity and this method is used to store
+ * global persistent data (in content providers, files, etc.)
+ *
+ * <p>After receiving this call you will usually receive a following call
+ * to {@link #onStop} (after the next activity has been resumed and
+ * displayed), however in some cases there will be a direct call back to
+ * {@link #onResume} without going through the stopped state.
+ *
+ * <p><em>Derived classes must call through to the super class's
+ * implementation of this method. If they do not, an exception will be
+ * thrown.</em></p>
+ *
+ * @see #onResume
+ * @see #onSaveInstanceState
+ * @see #onStop
+ */
+ protected void onPause() {
+ mCalled = true;
+ }
+
+ /**
+ * Called as part of the activity lifecycle when an activity is about to go
+ * into the background as the result of user choice. For example, when the
+ * user presses the Home key, {@link #onUserLeaveHint} will be called, but
+ * when an incoming phone call causes the in-call Activity to be automatically
+ * brought to the foreground, {@link #onUserLeaveHint} will not be called on
+ * the activity being interrupted. In cases when it is invoked, this method
+ * is called right before the activity's {@link #onPause} callback.
+ *
+ * <p>This callback and {@link #onUserInteraction} are intended to help
+ * activities manage status bar notifications intelligently; specifically,
+ * for helping activities determine the proper time to cancel a notfication.
+ *
+ * @see #onUserInteraction()
+ */
+ protected void onUserLeaveHint() {
+ }
+
+ /**
+ * Generate a new thumbnail for this activity. This method is called before
+ * pausing the activity, and should draw into <var>outBitmap</var> the
+ * imagery for the desired thumbnail in the dimensions of that bitmap. It
+ * can use the given <var>canvas</var>, which is configured to draw into the
+ * bitmap, for rendering if desired.
+ *
+ * <p>The default implementation renders the Screen's current view
+ * hierarchy into the canvas to generate a thumbnail.
+ *
+ * <p>If you return false, the bitmap will be filled with a default
+ * thumbnail.
+ *
+ * @param outBitmap The bitmap to contain the thumbnail.
+ * @param canvas Can be used to render into the bitmap.
+ *
+ * @return Return true if you have drawn into the bitmap; otherwise after
+ * you return it will be filled with a default thumbnail.
+ *
+ * @see #onCreateDescription
+ * @see #onSaveInstanceState
+ * @see #onPause
+ */
+ public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) {
+ final View view = mDecor;
+ if (view == null) {
+ return false;
+ }
+
+ final int vw = view.getWidth();
+ final int vh = view.getHeight();
+ final int dw = outBitmap.getWidth();
+ final int dh = outBitmap.getHeight();
+
+ canvas.save();
+ canvas.scale(((float)dw)/vw, ((float)dh)/vh);
+ view.draw(canvas);
+ canvas.restore();
+
+ return true;
+ }
+
+ /**
+ * Generate a new description for this activity. This method is called
+ * before pausing the activity and can, if desired, return some textual
+ * description of its current state to be displayed to the user.
+ *
+ * <p>The default implementation returns null, which will cause you to
+ * inherit the description from the previous activity. If all activities
+ * return null, generally the label of the top activity will be used as the
+ * description.
+ *
+ * @return A description of what the user is doing. It should be short and
+ * sweet (only a few words).
+ *
+ * @see #onCreateThumbnail
+ * @see #onSaveInstanceState
+ * @see #onPause
+ */
+ public CharSequence onCreateDescription() {
+ return null;
+ }
+
+ /**
+ * Called when you are no longer visible to the user. You will next
+ * receive either {@link #onRestart}, {@link #onDestroy}, or nothing,
+ * depending on later user activity.
+ *
+ * <p>Note that this method may never be called, in low memory situations
+ * where the system does not have enough memory to keep your activity's
+ * process running after its {@link #onPause} method is called.
+ *
+ * <p><em>Derived classes must call through to the super class's
+ * implementation of this method. If they do not, an exception will be
+ * thrown.</em></p>
+ *
+ * @see #onRestart
+ * @see #onResume
+ * @see #onSaveInstanceState
+ * @see #onDestroy
+ */
+ protected void onStop() {
+ mCalled = true;
+ }
+
+ /**
+ * Perform any final cleanup before an activity is destroyed. This can
+ * happen either because the activity is finishing (someone called
+ * {@link #finish} on it, or because the system is temporarily destroying
+ * this instance of the activity to save space. You can distinguish
+ * between these two scenarios with the {@link #isFinishing} method.
+ *
+ * <p><em>Note: do not count on this method being called as a place for
+ * saving data! For example, if an activity is editing data in a content
+ * provider, those edits should be committed in either {@link #onPause} or
+ * {@link #onSaveInstanceState}, not here.</em> This method is usually implemented to
+ * free resources like threads that are associated with an activity, so
+ * that a destroyed activity does not leave such things around while the
+ * rest of its application is still running. There are situations where
+ * the system will simply kill the activity's hosting process without
+ * calling this method (or any others) in it, so it should not be used to
+ * do things that are intended to remain around after the process goes
+ * away.
+ *
+ * <p><em>Derived classes must call through to the super class's
+ * implementation of this method. If they do not, an exception will be
+ * thrown.</em></p>
+ *
+ * @see #onPause
+ * @see #onStop
+ * @see #finish
+ * @see #isFinishing
+ */
+ protected void onDestroy() {
+ mCalled = true;
+
+ // dismiss any dialogs we are managing.
+ if (mManagedDialogs != null) {
+
+ final int numDialogs = mManagedDialogs.size();
+ for (int i = 0; i < numDialogs; i++) {
+ final Dialog dialog = mManagedDialogs.valueAt(i);
+ if (dialog.isShowing()) {
+ dialog.dismiss();
+ }
+ }
+ }
+
+ // also dismiss search dialog if showing
+ // TODO more generic than just this manager
+ SearchManager searchManager =
+ (SearchManager) getSystemService(Context.SEARCH_SERVICE);
+ searchManager.stopSearch();
+
+ // close any cursors we are managing.
+ int numCursors = mManagedCursors.size();
+ for (int i = 0; i < numCursors; i++) {
+ ManagedCursor c = mManagedCursors.get(i);
+ if (c != null) {
+ c.mCursor.close();
+ }
+ }
+ }
+
+ /**
+ * Called by the system when the device configuration changes while your
+ * activity is running. Note that this will <em>only</em> be called if
+ * you have selected configurations you would like to handle with the
+ * {@link android.R.attr#configChanges} attribute in your manifest. If
+ * any configuration change occurs that is not selected to be reported
+ * by that attribute, then instead of reporting it the system will stop
+ * and restart the activity (to have it launched with the new
+ * configuration).
+ *
+ * <p>At the time that this function has been called, your Resources
+ * object will have been updated to return resource values matching the
+ * new configuration.
+ *
+ * @param newConfig The new device configuration.
+ */
+ public void onConfigurationChanged(Configuration newConfig) {
+ mCalled = true;
+
+ // also update search dialog if showing
+ // TODO more generic than just this manager
+ SearchManager searchManager =
+ (SearchManager) getSystemService(Context.SEARCH_SERVICE);
+ searchManager.onConfigurationChanged(newConfig);
+
+ if (mWindow != null) {
+ // Pass the configuration changed event to the window
+ mWindow.onConfigurationChanged(newConfig);
+ }
+ }
+
+ /**
+ * If this activity is being destroyed because it can not handle a
+ * configuration parameter being changed (and thus its
+ * {@link #onConfigurationChanged(Configuration)} method is
+ * <em>not</em> being called), then you can use this method to discover
+ * the set of changes that have occurred while in the process of being
+ * destroyed. Note that there is no guarantee that these will be
+ * accurate (other changes could have happened at any time), so you should
+ * only use this as an optimization hint.
+ *
+ * @return Returns a bit field of the configuration parameters that are
+ * changing, as defined by the {@link android.content.res.Configuration}
+ * class.
+ */
+ public int getChangingConfigurations() {
+ return mConfigChangeFlags;
+ }
+
+ /**
+ * Retrieve the non-configuration instance data that was previously
+ * returned by {@link #onRetainNonConfigurationInstance()}. This will
+ * be available from the initial {@link #onCreate} and
+ * {@link #onStart} calls to the new instance, allowing you to extract
+ * any useful dynamic state from the previous instance.
+ *
+ * <p>Note that the data you retrieve here should <em>only</em> be used
+ * as an optimization for handling configuration changes. You should always
+ * be able to handle getting a null pointer back, and an activity must
+ * still be able to restore itself to its previous state (through the
+ * normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this
+ * function returns null.
+ *
+ * @return Returns the object previously returned by
+ * {@link #onRetainNonConfigurationInstance()}.
+ */
+ public Object getLastNonConfigurationInstance() {
+ return mLastNonConfigurationInstance;
+ }
+
+ /**
+ * Called by the system, as part of destroying an
+ * activity due to a configuration change, when it is known that a new
+ * instance will immediately be created for the new configuration. You
+ * can return any object you like here, including the activity instance
+ * itself, which can later be retrieved by calling
+ * {@link #getLastNonConfigurationInstance()} in the new activity
+ * instance.
+ *
+ * <p>This function is called purely as an optimization, and you must
+ * not rely on it being called. When it is called, a number of guarantees
+ * will be made to help optimize configuration switching:
+ * <ul>
+ * <li> The function will be called between {@link #onStop} and
+ * {@link #onDestroy}.
+ * <li> A new instance of the activity will <em>always</em> be immediately
+ * created after this one's {@link #onDestroy()} is called.
+ * <li> The object you return here will <em>always</em> be available from
+ * the {@link #getLastNonConfigurationInstance()} method of the following
+ * activity instance as described there.
+ * </ul>
+ *
+ * <p>These guarantees are designed so that an activity can use this API
+ * to propagate extensive state from the old to new activity instance, from
+ * loaded bitmaps, to network connections, to evenly actively running
+ * threads. Note that you should <em>not</em> propagate any data that
+ * may change based on the configuration, including any data loaded from
+ * resources such as strings, layouts, or drawables.
+ *
+ * @return Return any Object holding the desired state to propagate to the
+ * next activity instance.
+ */
+ public Object onRetainNonConfigurationInstance() {
+ return null;
+ }
+
+ /**
+ * Retrieve the non-configuration instance data that was previously
+ * returned by {@link #onRetainNonConfigurationChildInstances()}. This will
+ * be available from the initial {@link #onCreate} and
+ * {@link #onStart} calls to the new instance, allowing you to extract
+ * any useful dynamic state from the previous instance.
+ *
+ * <p>Note that the data you retrieve here should <em>only</em> be used
+ * as an optimization for handling configuration changes. You should always
+ * be able to handle getting a null pointer back, and an activity must
+ * still be able to restore itself to its previous state (through the
+ * normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this
+ * function returns null.
+ *
+ * @return Returns the object previously returned by
+ * {@link #onRetainNonConfigurationChildInstances()}
+ */
+ HashMap<String,Object> getLastNonConfigurationChildInstances() {
+ return mLastNonConfigurationChildInstances;
+ }
+
+ /**
+ * This method is similar to {@link #onRetainNonConfigurationInstance()} except that
+ * it should return either a mapping from child activity id strings to arbitrary objects,
+ * or null. This method is intended to be used by Activity framework subclasses that control a
+ * set of child activities, such as ActivityGroup. The same guarantees and restrictions apply
+ * as for {@link #onRetainNonConfigurationInstance()}. The default implementation returns null.
+ */
+ HashMap<String,Object> onRetainNonConfigurationChildInstances() {
+ return null;
+ }
+
+ public void onLowMemory() {
+ mCalled = true;
+ }
+
+ /**
+ * Wrapper around
+ * {@link ContentResolver#query(android.net.Uri , String[], String, String[], String)}
+ * that gives the resulting {@link Cursor} to call
+ * {@link #startManagingCursor} so that the activity will manage its
+ * lifecycle for you.
+ *
+ * @param uri The URI of the content provider to query.
+ * @param projection List of columns to return.
+ * @param selection SQL WHERE clause.
+ * @param sortOrder SQL ORDER BY clause.
+ *
+ * @return The Cursor that was returned by query().
+ *
+ * @see ContentResolver#query(android.net.Uri , String[], String, String[], String)
+ * @see #startManagingCursor
+ * @hide
+ */
+ public final Cursor managedQuery(Uri uri,
+ String[] projection,
+ String selection,
+ String sortOrder)
+ {
+ Cursor c = getContentResolver().query(uri, projection, selection, null, sortOrder);
+ if (c != null) {
+ startManagingCursor(c);
+ }
+ return c;
+ }
+
+ /**
+ * Wrapper around
+ * {@link ContentResolver#query(android.net.Uri , String[], String, String[], String)}
+ * that gives the resulting {@link Cursor} to call
+ * {@link #startManagingCursor} so that the activity will manage its
+ * lifecycle for you.
+ *
+ * @param uri The URI of the content provider to query.
+ * @param projection List of columns to return.
+ * @param selection SQL WHERE clause.
+ * @param selectionArgs The arguments to selection, if any ?s are pesent
+ * @param sortOrder SQL ORDER BY clause.
+ *
+ * @return The Cursor that was returned by query().
+ *
+ * @see ContentResolver#query(android.net.Uri , String[], String, String[], String)
+ * @see #startManagingCursor
+ */
+ public final Cursor managedQuery(Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String sortOrder)
+ {
+ Cursor c = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
+ if (c != null) {
+ startManagingCursor(c);
+ }
+ return c;
+ }
+
+ /**
+ * Wrapper around {@link Cursor#commitUpdates()} that takes care of noting
+ * that the Cursor needs to be requeried. You can call this method in
+ * {@link #onPause} or {@link #onStop} to have the system call
+ * {@link Cursor#requery} for you if the activity is later resumed. This
+ * allows you to avoid determing when to do the requery yourself (which is
+ * required for the Cursor to see any data changes that were committed with
+ * it).
+ *
+ * @param c The Cursor whose changes are to be committed.
+ *
+ * @see #managedQuery(android.net.Uri , String[], String, String[], String)
+ * @see #startManagingCursor
+ * @see Cursor#commitUpdates()
+ * @see Cursor#requery
+ * @hide
+ */
+ @Deprecated
+ public void managedCommitUpdates(Cursor c) {
+ synchronized (mManagedCursors) {
+ final int N = mManagedCursors.size();
+ for (int i=0; i<N; i++) {
+ ManagedCursor mc = mManagedCursors.get(i);
+ if (mc.mCursor == c) {
+ c.commitUpdates();
+ mc.mUpdated = true;
+ return;
+ }
+ }
+ throw new RuntimeException(
+ "Cursor " + c + " is not currently managed");
+ }
+ }
+
+ /**
+ * This method allows the activity to take care of managing the given
+ * {@link Cursor}'s lifecycle for you based on the activity's lifecycle.
+ * That is, when the activity is stopped it will automatically call
+ * {@link Cursor#deactivate} on the given Cursor, and when it is later restarted
+ * it will call {@link Cursor#requery} for you. When the activity is
+ * destroyed, all managed Cursors will be closed automatically.
+ *
+ * @param c The Cursor to be managed.
+ *
+ * @see #managedQuery(android.net.Uri , String[], String, String[], String)
+ * @see #stopManagingCursor
+ */
+ public void startManagingCursor(Cursor c) {
+ synchronized (mManagedCursors) {
+ mManagedCursors.add(new ManagedCursor(c));
+ }
+ }
+
+ /**
+ * Given a Cursor that was previously given to
+ * {@link #startManagingCursor}, stop the activity's management of that
+ * cursor.
+ *
+ * @param c The Cursor that was being managed.
+ *
+ * @see #startManagingCursor
+ */
+ public void stopManagingCursor(Cursor c) {
+ synchronized (mManagedCursors) {
+ final int N = mManagedCursors.size();
+ for (int i=0; i<N; i++) {
+ ManagedCursor mc = mManagedCursors.get(i);
+ if (mc.mCursor == c) {
+ mManagedCursors.remove(i);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Control whether this activity is required to be persistent. By default
+ * activities are not persistent; setting this to true will prevent the
+ * system from stopping this activity or its process when running low on
+ * resources.
+ *
+ * <p><em>You should avoid using this method</em>, it has severe negative
+ * consequences on how well the system can manage its resources. A better
+ * approach is to implement an application service that you control with
+ * {@link Context#startService} and {@link Context#stopService}.
+ *
+ * @param isPersistent Control whether the current activity must be
+ * persistent, true if so, false for the normal
+ * behavior.
+ */
+ public void setPersistent(boolean isPersistent) {
+ if (mParent == null) {
+ try {
+ ActivityManagerNative.getDefault()
+ .setPersistent(mToken, isPersistent);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ } else {
+ throw new RuntimeException("setPersistent() not yet supported for embedded activities");
+ }
+ }
+
+ /**
+ * Finds a view that was identified by the id attribute from the XML that
+ * was processed in {@link #onCreate}.
+ *
+ * @return The view if found or null otherwise.
+ */
+ public View findViewById(int id) {
+ return getWindow().findViewById(id);
+ }
+
+ /**
+ * Set the activity content from a layout resource. The resource will be
+ * inflated, adding all top-level views to the activity.
+ *
+ * @param layoutResID Resource ID to be inflated.
+ */
+ public void setContentView(int layoutResID) {
+ getWindow().setContentView(layoutResID);
+ }
+
+ /**
+ * Set the activity content to an explicit view. This view is placed
+ * directly into the activity's view hierarchy. It can itself be a complex
+ * view hierarhcy.
+ *
+ * @param view The desired content to display.
+ */
+ public void setContentView(View view) {
+ getWindow().setContentView(view);
+ }
+
+ /**
+ * Set the activity content to an explicit view. This view is placed
+ * directly into the activity's view hierarchy. It can itself be a complex
+ * view hierarhcy.
+ *
+ * @param view The desired content to display.
+ * @param params Layout parameters for the view.
+ */
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ getWindow().setContentView(view, params);
+ }
+
+ /**
+ * Add an additional content view to the activity. Added after any existing
+ * ones in the activity -- existing views are NOT removed.
+ *
+ * @param view The desired content to display.
+ * @param params Layout parameters for the view.
+ */
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ getWindow().addContentView(view, params);
+ }
+
+ /**
+ * Use with {@link #setDefaultKeyMode} to turn off default handling of
+ * keys.
+ *
+ * @see #setDefaultKeyMode
+ */
+ static public final int DEFAULT_KEYS_DISABLE = 0;
+ /**
+ * Use with {@link #setDefaultKeyMode} to launch the dialer during default
+ * key handling.
+ *
+ * @see #setDefaultKeyMode
+ */
+ static public final int DEFAULT_KEYS_DIALER = 1;
+ /**
+ * Use with {@link #setDefaultKeyMode} to execute a menu shortcut in
+ * default key handling.
+ *
+ * <p>That is, the user does not need to hold down the menu key to execute menu shortcuts.
+ *
+ * @see #setDefaultKeyMode
+ */
+ static public final int DEFAULT_KEYS_SHORTCUT = 2;
+ /**
+ * Use with {@link #setDefaultKeyMode} to specify that unhandled keystrokes
+ * will start an application-defined search. (If the application or activity does not
+ * actually define a search, the the keys will be ignored.)
+ *
+ * <p>See {@link android.app.SearchManager android.app.SearchManager} for more details.
+ *
+ * @see #setDefaultKeyMode
+ */
+ static public final int DEFAULT_KEYS_SEARCH_LOCAL = 3;
+
+ /**
+ * Use with {@link #setDefaultKeyMode} to specify that unhandled keystrokes
+ * will start a global search (typically web search, but some platforms may define alternate
+ * methods for global search)
+ *
+ * <p>See {@link android.app.SearchManager android.app.SearchManager} for more details.
+ *
+ * @see #setDefaultKeyMode
+ */
+ static public final int DEFAULT_KEYS_SEARCH_GLOBAL = 4;
+
+ /**
+ * Select the default key handling for this activity. This controls what
+ * will happen to key events that are not otherwise handled. The default
+ * mode ({@link #DEFAULT_KEYS_DISABLE}) will simply drop them on the
+ * floor. Other modes allow you to launch the dialer
+ * ({@link #DEFAULT_KEYS_DIALER}), execute a shortcut in your options
+ * menu without requiring the menu key be held down
+ * ({@link #DEFAULT_KEYS_SHORTCUT}), or launch a search ({@link #DEFAULT_KEYS_SEARCH_LOCAL}
+ * and {@link #DEFAULT_KEYS_SEARCH_GLOBAL}).
+ *
+ * <p>Note that the mode selected here does not impact the default
+ * handling of system keys, such as the "back" and "menu" keys, and your
+ * activity and its views always get a first chance to receive and handle
+ * all application keys.
+ *
+ * @param mode The desired default key mode constant.
+ *
+ * @see #DEFAULT_KEYS_DISABLE
+ * @see #DEFAULT_KEYS_DIALER
+ * @see #DEFAULT_KEYS_SHORTCUT
+ * @see #DEFAULT_KEYS_SEARCH_LOCAL
+ * @see #DEFAULT_KEYS_SEARCH_GLOBAL
+ * @see #onKeyDown
+ */
+ public final void setDefaultKeyMode(int mode) {
+ mDefaultKeyMode = mode;
+
+ // Some modes use a SpannableStringBuilder to track & dispatch input events
+ // This list must remain in sync with the switch in onKeyDown()
+ switch (mode) {
+ case DEFAULT_KEYS_DISABLE:
+ case DEFAULT_KEYS_SHORTCUT:
+ mDefaultKeySsb = null; // not used in these modes
+ break;
+ case DEFAULT_KEYS_DIALER:
+ case DEFAULT_KEYS_SEARCH_LOCAL:
+ case DEFAULT_KEYS_SEARCH_GLOBAL:
+ mDefaultKeySsb = new SpannableStringBuilder();
+ Selection.setSelection(mDefaultKeySsb,0);
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Called when a key was pressed down and not handled by any of the views
+ * inside of the activity. So, for example, key presses while the cursor
+ * is inside a TextView will not trigger the event (unless it is a navigation
+ * to another object) because TextView handles its own key presses.
+ *
+ * <p>If the focused view didn't want this event, this method is called.
+ *
+ * <p>The default implementation handles KEYCODE_BACK to stop the activity
+ * and go back, and other default key handling if configured with {@link #setDefaultKeyMode}.
+ *
+ * @return Return <code>true</code> to prevent this event from being propagated
+ * further, or <code>false</code> to indicate that you have not handled
+ * this event and it should continue to be propagated.
+ * @see #onKeyUp
+ * @see android.view.KeyEvent
+ */
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
+ finish();
+ return true;
+ }
+
+ if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) {
+ return false;
+ } else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) {
+ return getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,
+ keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE);
+ } else {
+ // Common code for DEFAULT_KEYS_DIALER & DEFAULT_KEYS_SEARCH_*
+ boolean clearSpannable = false;
+ boolean handled;
+ if ((event.getRepeatCount() != 0) || event.isSystem()) {
+ clearSpannable = true;
+ handled = false;
+ } else {
+ handled = TextKeyListener.getInstance().onKeyDown(null, mDefaultKeySsb,
+ keyCode, event);
+ if (handled && mDefaultKeySsb.length() > 0) {
+ // something useable has been typed - dispatch it now.
+
+ final String str = mDefaultKeySsb.toString();
+ clearSpannable = true;
+
+ switch (mDefaultKeyMode) {
+ case DEFAULT_KEYS_DIALER:
+ Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + str));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ break;
+ case DEFAULT_KEYS_SEARCH_LOCAL:
+ startSearch(str, false, null, false);
+ break;
+ case DEFAULT_KEYS_SEARCH_GLOBAL:
+ startSearch(str, false, null, true);
+ break;
+ }
+ }
+ }
+ if (clearSpannable) {
+ mDefaultKeySsb.clear();
+ mDefaultKeySsb.clearSpans();
+ Selection.setSelection(mDefaultKeySsb,0);
+ }
+ return handled;
+ }
+ }
+
+ /**
+ * Called when a key was released and not handled by any of the views
+ * inside of the activity. So, for example, key presses while the cursor
+ * is inside a TextView will not trigger the event (unless it is a navigation
+ * to another object) because TextView handles its own key presses.
+ *
+ * @return Return <code>true</code> to prevent this event from being propagated
+ * further, or <code>false</code> to indicate that you have not handled
+ * this event and it should continue to be propagated.
+ * @see #onKeyDown
+ * @see KeyEvent
+ */
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
+ * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle
+ * the event).
+ */
+ public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Called when a touch screen event was not handled by any of the views
+ * under it. This is most useful to process touch events that happen
+ * outside of your window bounds, where there is no view to receive it.
+ *
+ * @param event The touch screen event being processed.
+ *
+ * @return Return true if you have consumed the event, false if you haven't.
+ * The default implementation always returns false.
+ */
+ public boolean onTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * Called when the trackball was moved and not handled by any of the
+ * views inside of the activity. So, for example, if the trackball moves
+ * while focus is on a button, you will receive a call here because
+ * buttons do not normally do anything with trackball events. The call
+ * here happens <em>before</em> trackball movements are converted to
+ * DPAD key events, which then get sent back to the view hierarchy, and
+ * will be processed at the point for things like focus navigation.
+ *
+ * @param event The trackball event being processed.
+ *
+ * @return Return true if you have consumed the event, false if you haven't.
+ * The default implementation always returns false.
+ */
+ public boolean onTrackballEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * Called whenever a key, touch, or trackball event is dispatched to the
+ * activity. Implement this method if you wish to know that the user has
+ * interacted with the device in some way while your activity is running.
+ * This callback and {@link #onUserLeaveHint} are intended to help
+ * activities manage status bar notifications intelligently; specifically,
+ * for helping activities determine the proper time to cancel a notfication.
+ *
+ * <p>All calls to your activity's {@link #onUserLeaveHint} callback will
+ * be accompanied by calls to {@link #onUserInteraction}. This
+ * ensures that your activity will be told of relevant user activity such
+ * as pulling down the notification pane and touching an item there.
+ *
+ * <p>Note that this callback will be invoked for the touch down action
+ * that begins a touch gesture, but may not be invoked for the touch-moved
+ * and touch-up actions that follow.
+ *
+ * @see #onUserLeaveHint()
+ */
+ public void onUserInteraction() {
+ }
+
+ public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
+ // Update window manager if: we have a view, that view is
+ // attached to its parent (which will be a RootView), and
+ // this activity is not embedded.
+ if (mParent == null) {
+ View decor = mDecor;
+ if (decor != null && decor.getParent() != null) {
+ getWindowManager().updateViewLayout(decor, params);
+ }
+ }
+ }
+
+ public void onContentChanged() {
+ }
+
+ /**
+ * Called when the current {@link Window} of the activity gains or loses
+ * focus. This is the best indicator of whether this activity is visible
+ * to the user.
+ *
+ * <p>Note that this provides information what global focus state, which
+ * is managed independently of activity lifecycles. As such, while focus
+ * changes will generally have some relation to lifecycle changes (an
+ * activity that is stopped will not generally get window focus), you
+ * should not rely on any particular order between the callbacks here and
+ * those in the other lifecycle methods such as {@link #onResume}.
+ *
+ * <p>As a general rule, however, a resumed activity will have window
+ * focus... unless it has displayed other dialogs or popups that take
+ * input focus, in which case the activity itself will not have focus
+ * when the other windows have it. Likewise, the system may display
+ * system-level windows (such as the status bar notification panel or
+ * a system alert) which will temporarily take window input focus without
+ * pausing the foreground activity.
+ *
+ * @param hasFocus Whether the window of this activity has focus.
+ *
+ * @see #hasWindowFocus()
+ * @see #onResume
+ */
+ public void onWindowFocusChanged(boolean hasFocus) {
+ }
+
+ /**
+ * Returns true if this activity's <em>main</em> window currently has window focus.
+ * Note that this is not the same as the view itself having focus.
+ *
+ * @return True if this activity's main window currently has window focus.
+ *
+ * @see #onWindowAttributesChanged(android.view.WindowManager.LayoutParams)
+ */
+ public boolean hasWindowFocus() {
+ Window w = getWindow();
+ if (w != null) {
+ View d = w.getDecorView();
+ if (d != null) {
+ return d.hasWindowFocus();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called to process key events. You can override this to intercept all
+ * key events before they are dispatched to the window. Be sure to call
+ * this implementation for key events that should be handled normally.
+ *
+ * @param event The key event.
+ *
+ * @return boolean Return true if this event was consumed.
+ */
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ onUserInteraction();
+ if (getWindow().superDispatchKeyEvent(event)) {
+ return true;
+ }
+ return event.dispatch(this);
+ }
+
+ /**
+ * Called to process touch screen events. You can override this to
+ * intercept all touch screen events before they are dispatched to the
+ * window. Be sure to call this implementation for touch screen events
+ * that should be handled normally.
+ *
+ * @param ev The touch screen event.
+ *
+ * @return boolean Return true if this event was consumed.
+ */
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ onUserInteraction();
+ }
+ if (getWindow().superDispatchTouchEvent(ev)) {
+ return true;
+ }
+ return onTouchEvent(ev);
+ }
+
+ /**
+ * Called to process trackball events. You can override this to
+ * intercept all trackball events before they are dispatched to the
+ * window. Be sure to call this implementation for trackball events
+ * that should be handled normally.
+ *
+ * @param ev The trackball event.
+ *
+ * @return boolean Return true if this event was consumed.
+ */
+ public boolean dispatchTrackballEvent(MotionEvent ev) {
+ onUserInteraction();
+ if (getWindow().superDispatchTrackballEvent(ev)) {
+ return true;
+ }
+ return onTrackballEvent(ev);
+ }
+
+ /**
+ * Default implementation of
+ * {@link android.view.Window.Callback#onCreatePanelView}
+ * for activities. This
+ * simply returns null so that all panel sub-windows will have the default
+ * menu behavior.
+ */
+ public View onCreatePanelView(int featureId) {
+ return null;
+ }
+
+ /**
+ * Default implementation of
+ * {@link android.view.Window.Callback#onCreatePanelMenu}
+ * for activities. This calls through to the new
+ * {@link #onCreateOptionsMenu} method for the
+ * {@link android.view.Window#FEATURE_OPTIONS_PANEL} panel,
+ * so that subclasses of Activity don't need to deal with feature codes.
+ */
+ public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onCreateOptionsMenu(menu);
+ }
+ return false;
+ }
+
+ /**
+ * Default implementation of
+ * {@link android.view.Window.Callback#onPreparePanel}
+ * for activities. This
+ * calls through to the new {@link #onPrepareOptionsMenu} method for the
+ * {@link android.view.Window#FEATURE_OPTIONS_PANEL}
+ * panel, so that subclasses of
+ * Activity don't need to deal with feature codes.
+ */
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
+ boolean goforit = onPrepareOptionsMenu(menu);
+ return goforit && menu.hasVisibleItems();
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return The default implementation returns true.
+ */
+ public boolean onMenuOpened(int featureId, Menu menu) {
+ return true;
+ }
+
+ /**
+ * Default implementation of
+ * {@link android.view.Window.Callback#onMenuItemSelected}
+ * for activities. This calls through to the new
+ * {@link #onOptionsItemSelected} method for the
+ * {@link android.view.Window#FEATURE_OPTIONS_PANEL}
+ * panel, so that subclasses of
+ * Activity don't need to deal with feature codes.
+ */
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ switch (featureId) {
+ case Window.FEATURE_OPTIONS_PANEL:
+ // Put event logging here so it gets called even if subclass
+ // doesn't call through to superclass's implmeentation of each
+ // of these methods below
+ EventLog.writeEvent(50000, 0, item.getTitleCondensed());
+ return onOptionsItemSelected(item);
+
+ case Window.FEATURE_CONTEXT_MENU:
+ EventLog.writeEvent(50000, 1, item.getTitleCondensed());
+ return onContextItemSelected(item);
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Default implementation of
+ * {@link android.view.Window.Callback#onPanelClosed(int, Menu)} for
+ * activities. This calls through to {@link #onOptionsMenuClosed(Menu)}
+ * method for the {@link android.view.Window#FEATURE_OPTIONS_PANEL} panel,
+ * so that subclasses of Activity don't need to deal with feature codes.
+ * For context menus ({@link Window#FEATURE_CONTEXT_MENU}), the
+ * {@link #onContextMenuClosed(Menu)} will be called.
+ */
+ public void onPanelClosed(int featureId, Menu menu) {
+ switch (featureId) {
+ case Window.FEATURE_OPTIONS_PANEL:
+ onOptionsMenuClosed(menu);
+ break;
+
+ case Window.FEATURE_CONTEXT_MENU:
+ onContextMenuClosed(menu);
+ break;
+ }
+ }
+
+ /**
+ * Initialize the contents of the Activity's standard options menu. You
+ * should place your menu items in to <var>menu</var>.
+ *
+ * <p>This is only called once, the first time the options menu is
+ * displayed. To update the menu every time it is displayed, see
+ * {@link #onPrepareOptionsMenu}.
+ *
+ * <p>The default implementation populates the menu with standard system
+ * menu items. These are placed in the {@link Menu#CATEGORY_SYSTEM} group so that
+ * they will be correctly ordered with application-defined menu items.
+ * Deriving classes should always call through to the base implementation.
+ *
+ * <p>You can safely hold on to <var>menu</var> (and any items created
+ * from it), making modifications to it as desired, until the next
+ * time onCreateOptionsMenu() is called.
+ *
+ * <p>When you add items to the menu, you can implement the Activity's
+ * {@link #onOptionsItemSelected} method to handle them there.
+ *
+ * @param menu The options menu in which you place your items.
+ *
+ * @return You must return true for the menu to be displayed;
+ * if you return false it will not be shown.
+ *
+ * @see #onPrepareOptionsMenu
+ * @see #onOptionsItemSelected
+ */
+ public boolean onCreateOptionsMenu(Menu menu) {
+ if (mParent != null) {
+ return mParent.onCreateOptionsMenu(menu);
+ }
+ return true;
+ }
+
+ /**
+ * Prepare the Screen's standard options menu to be displayed. This is
+ * called right before the menu is shown, every time it is shown. You can
+ * use this method to efficiently enable/disable items or otherwise
+ * dynamically modify the contents.
+ *
+ * <p>The default implementation updates the system menu items based on the
+ * activity's state. Deriving classes should always call through to the
+ * base class implementation.
+ *
+ * @param menu The options menu as last shown or first initialized by
+ * onCreateOptionsMenu().
+ *
+ * @return You must return true for the menu to be displayed;
+ * if you return false it will not be shown.
+ *
+ * @see #onCreateOptionsMenu
+ */
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (mParent != null) {
+ return mParent.onPrepareOptionsMenu(menu);
+ }
+ return true;
+ }
+
+ /**
+ * This hook is called whenever an item in your options menu is selected.
+ * The default implementation simply returns false to have the normal
+ * processing happen (calling the item's Runnable or sending a message to
+ * its Handler as appropriate). You can use this method for any items
+ * for which you would like to do processing without those other
+ * facilities.
+ *
+ * <p>Derived classes should call through to the base class for it to
+ * perform the default menu handling.
+ *
+ * @param item The menu item that was selected.
+ *
+ * @return boolean Return false to allow normal menu processing to
+ * proceed, true to consume it here.
+ *
+ * @see #onCreateOptionsMenu
+ */
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (mParent != null) {
+ return mParent.onOptionsItemSelected(item);
+ }
+ return false;
+ }
+
+ /**
+ * This hook is called whenever the options menu is being closed (either by the user canceling
+ * the menu with the back/menu button, or when an item is selected).
+ *
+ * @param menu The options menu as last shown or first initialized by
+ * onCreateOptionsMenu().
+ */
+ public void onOptionsMenuClosed(Menu menu) {
+ if (mParent != null) {
+ mParent.onOptionsMenuClosed(menu);
+ }
+ }
+
+ /**
+ * Programmatically opens the options menu. If the options menu is already
+ * open, this method does nothing.
+ */
+ public void openOptionsMenu() {
+ mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
+ }
+
+ /**
+ * Progammatically closes the options menu. If the options menu is already
+ * closed, this method does nothing.
+ */
+ public void closeOptionsMenu() {
+ mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+ }
+
+ /**
+ * Called when a context menu for the {@code view} is about to be shown.
+ * Unlike {@link #onCreateOptionsMenu(Menu)}, this will be called every
+ * time the context menu is about to be shown and should be populated for
+ * the view (or item inside the view for {@link AdapterView} subclasses,
+ * this can be found in the {@code menuInfo})).
+ * <p>
+ * Use {@link #onContextItemSelected(android.view.MenuItem)} to know when an
+ * item has been selected.
+ * <p>
+ * It is not safe to hold onto the context menu after this method returns.
+ * {@inheritDoc}
+ */
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ }
+
+ /**
+ * Registers a context menu to be shown for the given view (multiple views
+ * can show the context menu). This method will set the
+ * {@link OnCreateContextMenuListener} on the view to this activity, so
+ * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be
+ * called when it is time to show the context menu.
+ *
+ * @see #unregisterForContextMenu(View)
+ * @param view The view that should show a context menu.
+ */
+ public void registerForContextMenu(View view) {
+ view.setOnCreateContextMenuListener(this);
+ }
+
+ /**
+ * Prevents a context menu to be shown for the given view. This method will remove the
+ * {@link OnCreateContextMenuListener} on the view.
+ *
+ * @see #registerForContextMenu(View)
+ * @param view The view that should stop showing a context menu.
+ */
+ public void unregisterForContextMenu(View view) {
+ view.setOnCreateContextMenuListener(null);
+ }
+
+ /**
+ * Programmatically opens the context menu for a particular {@code view}.
+ * The {@code view} should have been added via
+ * {@link #registerForContextMenu(View)}.
+ *
+ * @param view The view to show the context menu for.
+ */
+ public void openContextMenu(View view) {
+ view.showContextMenu();
+ }
+
+ /**
+ * Programmatically closes the most recently opened context menu, if showing.
+ */
+ public void closeContextMenu() {
+ mWindow.closePanel(Window.FEATURE_CONTEXT_MENU);
+ }
+
+ /**
+ * This hook is called whenever an item in a context menu is selected. The
+ * default implementation simply returns false to have the normal processing
+ * happen (calling the item's Runnable or sending a message to its Handler
+ * as appropriate). You can use this method for any items for which you
+ * would like to do processing without those other facilities.
+ * <p>
+ * Use {@link MenuItem#getMenuInfo()} to get extra information set by the
+ * View that added this menu item.
+ * <p>
+ * Derived classes should call through to the base class for it to perform
+ * the default menu handling.
+ *
+ * @param item The context menu item that was selected.
+ * @return boolean Return false to allow normal context menu processing to
+ * proceed, true to consume it here.
+ */
+ public boolean onContextItemSelected(MenuItem item) {
+ if (mParent != null) {
+ return mParent.onContextItemSelected(item);
+ }
+ return false;
+ }
+
+ /**
+ * This hook is called whenever the context menu is being closed (either by
+ * the user canceling the menu with the back/menu button, or when an item is
+ * selected).
+ *
+ * @param menu The context menu that is being closed.
+ */
+ public void onContextMenuClosed(Menu menu) {
+ if (mParent != null) {
+ mParent.onContextMenuClosed(menu);
+ }
+ }
+
+ /**
+ * Callback for creating dialogs that are managed (saved and restored) for you
+ * by the activity.
+ *
+ * If you use {@link #showDialog(int)}, the activity will call through to
+ * this method the first time, and hang onto it thereafter. Any dialog
+ * that is created by this method will automatically be saved and restored
+ * for you, including whether it is showing.
+ *
+ * If you would like the activity to manage the saving and restoring dialogs
+ * for you, you should override this method and handle any ids that are
+ * passed to {@link #showDialog}.
+ *
+ * If you would like an opportunity to prepare your dialog before it is shown,
+ * override {@link #onPrepareDialog(int, Dialog)}.
+ *
+ * @param id The id of the dialog.
+ * @return The dialog
+ *
+ * @see #onPrepareDialog(int, Dialog)
+ * @see #showDialog(int)
+ * @see #dismissDialog(int)
+ * @see #removeDialog(int)
+ */
+ protected Dialog onCreateDialog(int id) {
+ return null;
+ }
+
+ /**
+ * Provides an opportunity to prepare a managed dialog before it is being
+ * shown.
+ * <p>
+ * Override this if you need to update a managed dialog based on the state
+ * of the application each time it is shown. For example, a time picker
+ * dialog might want to be updated with the current time. You should call
+ * through to the superclass's implementation. The default implementation
+ * will set this Activity as the owner activity on the Dialog.
+ *
+ * @param id The id of the managed dialog.
+ * @param dialog The dialog.
+ * @see #onCreateDialog(int)
+ * @see #showDialog(int)
+ * @see #dismissDialog(int)
+ * @see #removeDialog(int)
+ */
+ protected void onPrepareDialog(int id, Dialog dialog) {
+ dialog.setOwnerActivity(this);
+ }
+
+ /**
+ * Show a dialog managed by this activity. A call to {@link #onCreateDialog(int)}
+ * will be made with the same id the first time this is called for a given
+ * id. From thereafter, the dialog will be automatically saved and restored.
+ *
+ * Each time a dialog is shown, {@link #onPrepareDialog(int, Dialog)} will
+ * be made to provide an opportunity to do any timely preparation.
+ *
+ * @param id The id of the managed dialog.
+ *
+ * @see #onCreateDialog(int)
+ * @see #onPrepareDialog(int, Dialog)
+ * @see #dismissDialog(int)
+ * @see #removeDialog(int)
+ */
+ public final void showDialog(int id) {
+ if (mManagedDialogs == null) {
+ mManagedDialogs = new SparseArray<Dialog>();
+ }
+ Dialog dialog = mManagedDialogs.get(id);
+ if (dialog == null) {
+ dialog = onCreateDialog(id);
+ if (dialog == null) {
+ throw new IllegalArgumentException("Activity#onCreateDialog did "
+ + "not create a dialog for id " + id);
+ }
+ dialog.dispatchOnCreate(null);
+ mManagedDialogs.put(id, dialog);
+ }
+
+ onPrepareDialog(id, dialog);
+ dialog.show();
+ }
+
+ /**
+ * Dismiss a dialog that was previously shown via {@link #showDialog(int)}.
+ *
+ * @param id The id of the managed dialog.
+ *
+ * @throws IllegalArgumentException if the id was not previously shown via
+ * {@link #showDialog(int)}.
+ *
+ * @see #onCreateDialog(int)
+ * @see #onPrepareDialog(int, Dialog)
+ * @see #showDialog(int)
+ * @see #removeDialog(int)
+ */
+ public final void dismissDialog(int id) {
+ if (mManagedDialogs == null) {
+ throw missingDialog(id);
+
+ }
+ final Dialog dialog = mManagedDialogs.get(id);
+ if (dialog == null) {
+ throw missingDialog(id);
+ }
+ dialog.dismiss();
+ }
+
+ /**
+ * Creates an exception to throw if a user passed in a dialog id that is
+ * unexpected.
+ */
+ private IllegalArgumentException missingDialog(int id) {
+ return new IllegalArgumentException("no dialog with id " + id + " was ever "
+ + "shown via Activity#showDialog");
+ }
+
+ /**
+ * Removes any internal references to a dialog managed by this Activity.
+ * If the dialog is showing, it will dismiss it as part of the clean up.
+ *
+ * This can be useful if you know that you will never show a dialog again and
+ * want to avoid the overhead of saving and restoring it in the future.
+ *
+ * @param id The id of the managed dialog.
+ *
+ * @see #onCreateDialog(int)
+ * @see #onPrepareDialog(int, Dialog)
+ * @see #showDialog(int)
+ * @see #dismissDialog(int)
+ */
+ public final void removeDialog(int id) {
+
+ if (mManagedDialogs == null) {
+ return;
+ }
+
+ final Dialog dialog = mManagedDialogs.get(id);
+ if (dialog == null) {
+ return;
+ }
+
+ dialog.dismiss();
+ mManagedDialogs.remove(id);
+ }
+
+ /**
+ * This hook is called when the user signals the desire to start a search.
+ *
+ * <p>You can use this function as a simple way to launch the search UI, in response to a
+ * menu item, search button, or other widgets within your activity. Unless overidden,
+ * calling this function is the same as calling:
+ * <p>The default implementation simply calls
+ * {@link #startSearch startSearch(null, false, null, false)}, launching a local search.
+ *
+ * <p>You can override this function to force global search, e.g. in response to a dedicated
+ * search key, or to block search entirely (by simply returning false).
+ *
+ * @return Returns true if search launched, false if activity blocks it
+ *
+ * @see android.app.SearchManager
+ */
+ public boolean onSearchRequested() {
+ startSearch(null, false, null, false);
+ return true;
+ }
+
+ /**
+ * This hook is called to launch the search UI.
+ *
+ * <p>It is typically called from onSearchRequested(), either directly from
+ * Activity.onSearchRequested() or from an overridden version in any given
+ * Activity. If your goal is simply to activate search, it is preferred to call
+ * onSearchRequested(), which may have been overriden elsewhere in your Activity. If your goal
+ * is to inject specific data such as context data, it is preferred to <i>override</i>
+ * onSearchRequested(), so that any callers to it will benefit from the override.
+ *
+ * @param initialQuery Any non-null non-empty string will be inserted as
+ * pre-entered text in the search query box.
+ * @param selectInitialQuery If true, the intial query will be preselected, which means that
+ * any further typing will replace it. This is useful for cases where an entire pre-formed
+ * query is being inserted. If false, the selection point will be placed at the end of the
+ * inserted query. This is useful when the inserted query is text that the user entered,
+ * and the user would expect to be able to keep typing. <i>This parameter is only meaningful
+ * if initialQuery is a non-empty string.</i>
+ * @param appSearchData An application can insert application-specific
+ * context here, in order to improve quality or specificity of its own
+ * searches. This data will be returned with SEARCH intent(s). Null if
+ * no extra data is required.
+ * @param globalSearch If false, this will only launch the search that has been specifically
+ * defined by the application (which is usually defined as a local search). If no default
+ * search is defined in the current application or activity, no search will be launched.
+ * If true, this will always launch a platform-global (e.g. web-based) search instead.
+ *
+ * @see android.app.SearchManager
+ * @see #onSearchRequested
+ */
+ public void startSearch(String initialQuery, boolean selectInitialQuery,
+ Bundle appSearchData, boolean globalSearch) {
+ // activate the search manager and start it up!
+ SearchManager searchManager = (SearchManager)
+ getSystemService(Context.SEARCH_SERVICE);
+ searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
+ appSearchData, globalSearch);
+ }
+
+ /**
+ * Request that key events come to this activity. Use this if your
+ * activity has no views with focus, but the activity still wants
+ * a chance to process key events.
+ *
+ * @see android.view.Window#takeKeyEvents
+ */
+ public void takeKeyEvents(boolean get) {
+ getWindow().takeKeyEvents(get);
+ }
+
+ /**
+ * Enable extended window features. This is a convenience for calling
+ * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
+ *
+ * @param featureId The desired feature as defined in
+ * {@link android.view.Window}.
+ * @return Returns true if the requested feature is supported and now
+ * enabled.
+ *
+ * @see android.view.Window#requestFeature
+ */
+ public final boolean requestWindowFeature(int featureId) {
+ return getWindow().requestFeature(featureId);
+ }
+
+ /**
+ * Convenience for calling
+ * {@link android.view.Window#setFeatureDrawableResource}.
+ */
+ public final void setFeatureDrawableResource(int featureId, int resId) {
+ getWindow().setFeatureDrawableResource(featureId, resId);
+ }
+
+ /**
+ * Convenience for calling
+ * {@link android.view.Window#setFeatureDrawableUri}.
+ */
+ public final void setFeatureDrawableUri(int featureId, Uri uri) {
+ getWindow().setFeatureDrawableUri(featureId, uri);
+ }
+
+ /**
+ * Convenience for calling
+ * {@link android.view.Window#setFeatureDrawable(int, Drawable)}.
+ */
+ public final void setFeatureDrawable(int featureId, Drawable drawable) {
+ getWindow().setFeatureDrawable(featureId, drawable);
+ }
+
+ /**
+ * Convenience for calling
+ * {@link android.view.Window#setFeatureDrawableAlpha}.
+ */
+ public final void setFeatureDrawableAlpha(int featureId, int alpha) {
+ getWindow().setFeatureDrawableAlpha(featureId, alpha);
+ }
+
+ /**
+ * Convenience for calling
+ * {@link android.view.Window#getLayoutInflater}.
+ */
+ public LayoutInflater getLayoutInflater() {
+ return getWindow().getLayoutInflater();
+ }
+
+ /**
+ * Returns a {@link MenuInflater} with this context.
+ */
+ public MenuInflater getMenuInflater() {
+ return new MenuInflater(this);
+ }
+
+ @Override
+ protected void onApplyThemeResource(Resources.Theme theme,
+ int resid,
+ boolean first)
+ {
+ if (mParent == null) {
+ super.onApplyThemeResource(theme, resid, first);
+ } else {
+ try {
+ theme.setTo(mParent.getTheme());
+ } catch (Exception e) {
+ // Empty
+ }
+ theme.applyStyle(resid, false);
+ }
+ }
+
+ /**
+ * Launch an activity for which you would like a result when it finished.
+ * When this activity exits, your
+ * onActivityResult() method will be called with the given requestCode.
+ * Using a negative requestCode is the same as calling
+ * {@link #startActivity} (the activity is not launched as a sub-activity).
+ *
+ * <p>Note that this method should only be used with Intent protocols
+ * that are defined to return a result. In other protocols (such as
+ * {@link Intent#ACTION_MAIN} or {@link Intent#ACTION_VIEW}), you may
+ * not get the result when you expect. For example, if the activity you
+ * are launching uses the singleTask launch mode, it will not run in your
+ * task and thus you will immediately receive a cancel result.
+ *
+ * <p>As a special case, if you call startActivityForResult() with a requestCode
+ * >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your
+ * activity, then your window will not be displayed until a result is
+ * returned back from the started activity. This is to avoid visible
+ * flickering when redirecting to another activity.
+ *
+ * <p>This method throws {@link android.content.ActivityNotFoundException}
+ * if there was no Activity found to run the given Intent.
+ *
+ * @param intent The intent to start.
+ * @param requestCode If >= 0, this code will be returned in
+ * onActivityResult() when the activity exits.
+ *
+ * @throws android.content.ActivityNotFoundException
+ *
+ * @see #startActivity
+ */
+ public void startActivityForResult(Intent intent, int requestCode) {
+ if (mParent == null) {
+ Instrumentation.ActivityResult ar =
+ mInstrumentation.execStartActivity(
+ this, mMainThread.getApplicationThread(), mToken, this,
+ intent, requestCode);
+ if (ar != null) {
+ mMainThread.sendActivityResult(
+ mToken, mEmbeddedID, requestCode, ar.getResultCode(),
+ ar.getResultData());
+ }
+ if (requestCode >= 0) {
+ // If this start is requesting a result, we can avoid making
+ // the activity visible until the result is received. Setting
+ // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
+ // activity hidden during this time, to avoid flickering.
+ // This can only be done when a result is requested because
+ // that guarantees we will get information back when the
+ // activity is finished, no matter what happens to it.
+ mStartedActivity = true;
+ }
+ } else {
+ mParent.startActivityFromChild(this, intent, requestCode);
+ }
+ }
+
+ /**
+ * Launch a new activity. You will not receive any information about when
+ * the activity exits. This implementation overrides the base version,
+ * providing information about
+ * the activity performing the launch. Because of this additional
+ * information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not
+ * required; if not specified, the new activity will be added to the
+ * task of the caller.
+ *
+ * <p>This method throws {@link android.content.ActivityNotFoundException}
+ * if there was no Activity found to run the given Intent.
+ *
+ * @param intent The intent to start.
+ *
+ * @throws android.content.ActivityNotFoundException
+ *
+ * @see #startActivityForResult
+ */
+ @Override
+ public void startActivity(Intent intent) {
+ startActivityForResult(intent, -1);
+ }
+
+ /**
+ * A special variation to launch an activity only if a new activity
+ * instance is needed to handle the given Intent. In other words, this is
+ * just like {@link #startActivityForResult(Intent, int)} except: if you are
+ * using the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP} flag, or
+ * singleTask or singleTop
+ * {@link android.R.styleable#AndroidManifestActivity_launchMode launchMode},
+ * and the activity
+ * that handles <var>intent</var> is the same as your currently running
+ * activity, then a new instance is not needed. In this case, instead of
+ * the normal behavior of calling {@link #onNewIntent} this function will
+ * return and you can handle the Intent yourself.
+ *
+ * <p>This function can only be called from a top-level activity; if it is
+ * called from a child activity, a runtime exception will be thrown.
+ *
+ * @param intent The intent to start.
+ * @param requestCode If >= 0, this code will be returned in
+ * onActivityResult() when the activity exits, as described in
+ * {@link #startActivityForResult}.
+ *
+ * @return If a new activity was launched then true is returned; otherwise
+ * false is returned and you must handle the Intent yourself.
+ *
+ * @see #startActivity
+ * @see #startActivityForResult
+ */
+ public boolean startActivityIfNeeded(Intent intent, int requestCode) {
+ if (mParent == null) {
+ int result = IActivityManager.START_RETURN_INTENT_TO_CALLER;
+ try {
+ result = ActivityManagerNative.getDefault()
+ .startActivity(mMainThread.getApplicationThread(),
+ intent, intent.resolveTypeIfNeeded(
+ getContentResolver()),
+ null, 0,
+ mToken, mEmbeddedID, requestCode, true, false);
+ } catch (RemoteException e) {
+ // Empty
+ }
+
+ Instrumentation.checkStartActivityResult(result, intent);
+
+ if (requestCode >= 0) {
+ // If this start is requesting a result, we can avoid making
+ // the activity visible until the result is received. Setting
+ // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
+ // activity hidden during this time, to avoid flickering.
+ // This can only be done when a result is requested because
+ // that guarantees we will get information back when the
+ // activity is finished, no matter what happens to it.
+ mStartedActivity = true;
+ }
+ return result != IActivityManager.START_RETURN_INTENT_TO_CALLER;
+ }
+
+ throw new UnsupportedOperationException(
+ "startActivityIfNeeded can only be called from a top-level activity");
+ }
+
+ /**
+ * Special version of starting an activity, for use when you are replacing
+ * other activity components. You can use this to hand the Intent off
+ * to the next Activity that can handle it. You typically call this in
+ * {@link #onCreate} with the Intent returned by {@link #getIntent}.
+ *
+ * @param intent The intent to dispatch to the next activity. For
+ * correct behavior, this must be the same as the Intent that started
+ * your own activity; the only changes you can make are to the extras
+ * inside of it.
+ *
+ * @return Returns a boolean indicating whether there was another Activity
+ * to start: true if there was a next activity to start, false if there
+ * wasn't. In general, if true is returned you will then want to call
+ * finish() on yourself.
+ */
+ public boolean startNextMatchingActivity(Intent intent) {
+ if (mParent == null) {
+ try {
+ return ActivityManagerNative.getDefault()
+ .startNextMatchingActivity(mToken, intent);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ return false;
+ }
+
+ throw new UnsupportedOperationException(
+ "startNextMatchingActivity can only be called from a top-level activity");
+ }
+
+ /**
+ * This is called when a child activity of this one calls its
+ * {@link #startActivity} or {@link #startActivityForResult} method.
+ *
+ * <p>This method throws {@link android.content.ActivityNotFoundException}
+ * if there was no Activity found to run the given Intent.
+ *
+ * @param child The activity making the call.
+ * @param intent The intent to start.
+ * @param requestCode Reply request code. < 0 if reply is not requested.
+ *
+ * @throws android.content.ActivityNotFoundException
+ *
+ * @see #startActivity
+ * @see #startActivityForResult
+ */
+ public void startActivityFromChild(Activity child, Intent intent,
+ int requestCode) {
+ Instrumentation.ActivityResult ar =
+ mInstrumentation.execStartActivity(
+ this, mMainThread.getApplicationThread(), mToken, child,
+ intent, requestCode);
+ if (ar != null) {
+ mMainThread.sendActivityResult(
+ mToken, child.mEmbeddedID, requestCode,
+ ar.getResultCode(), ar.getResultData());
+ }
+ }
+
+ /**
+ * Call this to set the result that your activity will return to its
+ * caller.
+ *
+ * @param resultCode The result code to propagate back to the originating
+ * activity, often RESULT_CANCELED or RESULT_OK
+ *
+ * @see #RESULT_CANCELED
+ * @see #RESULT_OK
+ * @see #RESULT_FIRST_USER
+ * @see #setResult(int, Intent)
+ */
+ public final void setResult(int resultCode) {
+ synchronized (this) {
+ mResultCode = resultCode;
+ mResultData = null;
+ }
+ }
+
+ /**
+ * Call this to set the result that your activity will return to its
+ * caller.
+ *
+ * @param resultCode The result code to propagate back to the originating
+ * activity, often RESULT_CANCELED or RESULT_OK
+ * @param data The data to propagate back to the originating activity.
+ *
+ * @see #RESULT_CANCELED
+ * @see #RESULT_OK
+ * @see #RESULT_FIRST_USER
+ * @see #setResult(int)
+ */
+ public final void setResult(int resultCode, Intent data) {
+ synchronized (this) {
+ mResultCode = resultCode;
+ mResultData = data;
+ }
+ }
+
+ /**
+ * Return the name of the package that invoked this activity. This is who
+ * the data in {@link #setResult setResult()} will be sent to. You can
+ * use this information to validate that the recipient is allowed to
+ * receive the data.
+ *
+ * <p>Note: if the calling activity is not expecting a result (that is it
+ * did not use the {@link #startActivityForResult}
+ * form that includes a request code), then the calling package will be
+ * null.
+ *
+ * @return The package of the activity that will receive your
+ * reply, or null if none.
+ */
+ public String getCallingPackage() {
+ try {
+ return ActivityManagerNative.getDefault().getCallingPackage(mToken);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Return the name of the activity that invoked this activity. This is
+ * who the data in {@link #setResult setResult()} will be sent to. You
+ * can use this information to validate that the recipient is allowed to
+ * receive the data.
+ *
+ * <p>Note: if the calling activity is not expecting a result (that is it
+ * did not use the {@link #startActivityForResult}
+ * form that includes a request code), then the calling package will be
+ * null.
+ *
+ * @return String The full name of the activity that will receive your
+ * reply, or null if none.
+ */
+ public ComponentName getCallingActivity() {
+ try {
+ return ActivityManagerNative.getDefault().getCallingActivity(mToken);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Control whether this activity's main window is visible. This is intended
+ * only for the special case of an activity that is not going to show a
+ * UI itself, but can't just finish prior to onResume() because it needs
+ * to wait for a service binding or such. Setting this to false allows
+ * you to prevent your UI from being shown during that time.
+ *
+ * <p>The default value for this is taken from the
+ * {@link android.R.attr#windowNoDisplay} attribute of the activity's theme.
+ */
+ public void setVisible(boolean visible) {
+ if (mVisibleFromClient != visible) {
+ mVisibleFromClient = visible;
+ if (mVisibleFromServer) {
+ if (visible) makeVisible();
+ else mDecor.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ void makeVisible() {
+ if (!mWindowAdded) {
+ ViewManager wm = getWindowManager();
+ wm.addView(mDecor, getWindow().getAttributes());
+ mWindowAdded = true;
+ }
+ mDecor.setVisibility(View.VISIBLE);
+ }
+
+ /**
+ * Check to see whether this activity is in the process of finishing,
+ * either because you called {@link #finish} on it or someone else
+ * has requested that it finished. This is often used in
+ * {@link #onPause} to determine whether the activity is simply pausing or
+ * completely finishing.
+ *
+ * @return If the activity is finishing, returns true; else returns false.
+ *
+ * @see #finish
+ */
+ public boolean isFinishing() {
+ return mFinished;
+ }
+
+ /**
+ * Call this when your activity is done and should be closed. The
+ * ActivityResult is propagated back to whoever launched you via
+ * onActivityResult().
+ */
+ public void finish() {
+ if (mParent == null) {
+ int resultCode;
+ Intent resultData;
+ synchronized (this) {
+ resultCode = mResultCode;
+ resultData = mResultData;
+ }
+ if (Config.LOGV) Log.v(TAG, "Finishing self: token=" + mToken);
+ try {
+ if (ActivityManagerNative.getDefault()
+ .finishActivity(mToken, resultCode, resultData)) {
+ mFinished = true;
+ }
+ } catch (RemoteException e) {
+ // Empty
+ }
+ } else {
+ mParent.finishFromChild(this);
+ }
+ }
+
+ /**
+ * This is called when a child activity of this one calls its
+ * {@link #finish} method. The default implementation simply calls
+ * finish() on this activity (the parent), finishing the entire group.
+ *
+ * @param child The activity making the call.
+ *
+ * @see #finish
+ */
+ public void finishFromChild(Activity child) {
+ finish();
+ }
+
+ /**
+ * Force finish another activity that you had previously started with
+ * {@link #startActivityForResult}.
+ *
+ * @param requestCode The request code of the activity that you had
+ * given to startActivityForResult(). If there are multiple
+ * activities started with this request code, they
+ * will all be finished.
+ */
+ public void finishActivity(int requestCode) {
+ if (mParent == null) {
+ try {
+ ActivityManagerNative.getDefault()
+ .finishSubActivity(mToken, mEmbeddedID, requestCode);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ } else {
+ mParent.finishActivityFromChild(this, requestCode);
+ }
+ }
+
+ /**
+ * This is called when a child activity of this one calls its
+ * finishActivity().
+ *
+ * @param child The activity making the call.
+ * @param requestCode Request code that had been used to start the
+ * activity.
+ */
+ public void finishActivityFromChild(Activity child, int requestCode) {
+ try {
+ ActivityManagerNative.getDefault()
+ .finishSubActivity(mToken, child.mEmbeddedID, requestCode);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ }
+
+ /**
+ * Called when an activity you launched exits, giving you the requestCode
+ * you started it with, the resultCode it returned, and any additional
+ * data from it. The <var>resultCode</var> will be
+ * {@link #RESULT_CANCELED} if the activity explicitly returned that,
+ * didn't return any result, or crashed during its operation.
+ *
+ * <p>You will receive this call immediately before onResume() when your
+ * activity is re-starting.
+ *
+ * @param requestCode The integer request code originally supplied to
+ * startActivityForResult(), allowing you to identify who this
+ * result came from.
+ * @param resultCode The integer result code returned by the child activity
+ * through its setResult().
+ * @param data An Intent, which can return result data to the caller
+ * (various data can be attached to Intent "extras").
+ *
+ * @see #startActivityForResult
+ * @see #createPendingResult
+ * @see #setResult(int)
+ */
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ }
+
+ /**
+ * Create a new PendingIntent object which you can hand to others
+ * for them to use to send result data back to your
+ * {@link #onActivityResult} callback. The created object will be either
+ * one-shot (becoming invalid after a result is sent back) or multiple
+ * (allowing any number of results to be sent through it).
+ *
+ * @param requestCode Private request code for the sender that will be
+ * associated with the result data when it is returned. The sender can not
+ * modify this value, allowing you to identify incoming results.
+ * @param data Default data to supply in the result, which may be modified
+ * by the sender.
+ * @param flags May be {@link PendingIntent#FLAG_ONE_SHOT PendingIntent.FLAG_ONE_SHOT},
+ * {@link PendingIntent#FLAG_NO_CREATE PendingIntent.FLAG_NO_CREATE},
+ * {@link PendingIntent#FLAG_CANCEL_CURRENT PendingIntent.FLAG_CANCEL_CURRENT},
+ * {@link PendingIntent#FLAG_UPDATE_CURRENT PendingIntent.FLAG_UPDATE_CURRENT},
+ * or any of the flags as supported by
+ * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
+ * of the intent that can be supplied when the actual send happens.
+ *
+ * @return Returns an existing or new PendingIntent matching the given
+ * parameters. May return null only if
+ * {@link PendingIntent#FLAG_NO_CREATE PendingIntent.FLAG_NO_CREATE} has been
+ * supplied.
+ *
+ * @see PendingIntent
+ */
+ public PendingIntent createPendingResult(int requestCode, Intent data,
+ int flags) {
+ String packageName = getPackageName();
+ try {
+ IIntentSender target =
+ ActivityManagerNative.getDefault().getIntentSender(
+ IActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
+ mParent == null ? mToken : mParent.mToken,
+ mEmbeddedID, requestCode, data, null, flags);
+ return target != null ? new PendingIntent(target) : null;
+ } catch (RemoteException e) {
+ // Empty
+ }
+ return null;
+ }
+
+ /**
+ * Change the desired orientation of this activity. If the activity
+ * is currently in the foreground or otherwise impacting the screen
+ * orientation, the screen will immediately be changed (possibly causing
+ * the activity to be restarted). Otherwise, this will be used the next
+ * time the activity is visible.
+ *
+ * @param requestedOrientation An orientation constant as used in
+ * {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}.
+ */
+ public void setRequestedOrientation(int requestedOrientation) {
+ if (mParent == null) {
+ try {
+ ActivityManagerNative.getDefault().setRequestedOrientation(
+ mToken, requestedOrientation);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ } else {
+ mParent.setRequestedOrientation(requestedOrientation);
+ }
+ }
+
+ /**
+ * Return the current requested orientation of the activity. This will
+ * either be the orientation requested in its component's manifest, or
+ * the last requested orientation given to
+ * {@link #setRequestedOrientation(int)}.
+ *
+ * @return Returns an orientation constant as used in
+ * {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}.
+ */
+ public int getRequestedOrientation() {
+ if (mParent == null) {
+ try {
+ return ActivityManagerNative.getDefault()
+ .getRequestedOrientation(mToken);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ } else {
+ return mParent.getRequestedOrientation();
+ }
+ return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ /**
+ * Return the identifier of the task this activity is in. This identifier
+ * will remain the same for the lifetime of the activity.
+ *
+ * @return Task identifier, an opaque integer.
+ */
+ public int getTaskId() {
+ try {
+ return ActivityManagerNative.getDefault()
+ .getTaskForActivity(mToken, false);
+ } catch (RemoteException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Return whether this activity is the root of a task. The root is the
+ * first activity in a task.
+ *
+ * @return True if this is the root activity, else false.
+ */
+ public boolean isTaskRoot() {
+ try {
+ return ActivityManagerNative.getDefault()
+ .getTaskForActivity(mToken, true) >= 0;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Move the task containing this activity to the back of the activity
+ * stack. The activity's order within the task is unchanged.
+ *
+ * @param nonRoot If false then this only works if the activity is the root
+ * of a task; if true it will work for any activity in
+ * a task.
+ *
+ * @return If the task was moved (or it was already at the
+ * back) true is returned, else false.
+ */
+ public boolean moveTaskToBack(boolean nonRoot) {
+ try {
+ return ActivityManagerNative.getDefault().moveActivityTaskToBack(
+ mToken, nonRoot);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ return false;
+ }
+
+ /**
+ * Returns class name for this activity with the package prefix removed.
+ * This is the default name used to read and write settings.
+ *
+ * @return The local class name.
+ */
+ public String getLocalClassName() {
+ final String pkg = getPackageName();
+ final String cls = mComponent.getClassName();
+ int packageLen = pkg.length();
+ if (!cls.startsWith(pkg) || cls.length() <= packageLen
+ || cls.charAt(packageLen) != '.') {
+ return cls;
+ }
+ return cls.substring(packageLen+1);
+ }
+
+ /**
+ * Returns complete component name of this activity.
+ *
+ * @return Returns the complete component name for this activity
+ */
+ public ComponentName getComponentName()
+ {
+ return mComponent;
+ }
+
+ /**
+ * Retrieve a {@link SharedPreferences} object for accessing preferences
+ * that are private to this activity. This simply calls the underlying
+ * {@link #getSharedPreferences(String, int)} method by passing in this activity's
+ * class name as the preferences name.
+ *
+ * @param mode Operating mode. Use {@link #MODE_PRIVATE} for the default
+ * operation, {@link #MODE_WORLD_READABLE} and
+ * {@link #MODE_WORLD_WRITEABLE} to control permissions.
+ *
+ * @return Returns the single SharedPreferences instance that can be used
+ * to retrieve and modify the preference values.
+ */
+ public SharedPreferences getPreferences(int mode) {
+ return getSharedPreferences(getLocalClassName(), mode);
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (getBaseContext() == null) {
+ throw new IllegalStateException(
+ "System services not available to Activities before onCreate()");
+ }
+
+ if (WINDOW_SERVICE.equals(name)) {
+ return mWindowManager;
+ }
+ return super.getSystemService(name);
+ }
+
+ /**
+ * Change the title associated with this activity. If this is a
+ * top-level activity, the title for its window will change. If it
+ * is an embedded activity, the parent can do whatever it wants
+ * with it.
+ */
+ public void setTitle(CharSequence title) {
+ mTitle = title;
+ onTitleChanged(title, mTitleColor);
+
+ if (mParent != null) {
+ mParent.onChildTitleChanged(this, title);
+ }
+ }
+
+ /**
+ * Change the title associated with this activity. If this is a
+ * top-level activity, the title for its window will change. If it
+ * is an embedded activity, the parent can do whatever it wants
+ * with it.
+ */
+ public void setTitle(int titleId) {
+ setTitle(getText(titleId));
+ }
+
+ public void setTitleColor(int textColor) {
+ mTitleColor = textColor;
+ onTitleChanged(mTitle, textColor);
+ }
+
+ public final CharSequence getTitle() {
+ return mTitle;
+ }
+
+ public final int getTitleColor() {
+ return mTitleColor;
+ }
+
+ protected void onTitleChanged(CharSequence title, int color) {
+ if (mTitleReady) {
+ final Window win = getWindow();
+ if (win != null) {
+ win.setTitle(title);
+ if (color != 0) {
+ win.setTitleColor(color);
+ }
+ }
+ }
+ }
+
+ protected void onChildTitleChanged(Activity childActivity, CharSequence title) {
+ }
+
+ /**
+ * Sets the visibility of the progress bar in the title.
+ * <p>
+ * In order for the progress bar to be shown, the feature must be requested
+ * via {@link #requestWindowFeature(int)}.
+ *
+ * @param visible Whether to show the progress bars in the title.
+ */
+ public final void setProgressBarVisibility(boolean visible) {
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS, visible ? Window.PROGRESS_VISIBILITY_ON :
+ Window.PROGRESS_VISIBILITY_OFF);
+ }
+
+ /**
+ * Sets the visibility of the indeterminate progress bar in the title.
+ * <p>
+ * In order for the progress bar to be shown, the feature must be requested
+ * via {@link #requestWindowFeature(int)}.
+ *
+ * @param visible Whether to show the progress bars in the title.
+ */
+ public final void setProgressBarIndeterminateVisibility(boolean visible) {
+ getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
+ visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF);
+ }
+
+ /**
+ * Sets whether the horizontal progress bar in the title should be indeterminate (the circular
+ * is always indeterminate).
+ * <p>
+ * In order for the progress bar to be shown, the feature must be requested
+ * via {@link #requestWindowFeature(int)}.
+ *
+ * @param indeterminate Whether the horizontal progress bar should be indeterminate.
+ */
+ public final void setProgressBarIndeterminate(boolean indeterminate) {
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
+ indeterminate ? Window.PROGRESS_INDETERMINATE_ON : Window.PROGRESS_INDETERMINATE_OFF);
+ }
+
+ /**
+ * Sets the progress for the progress bars in the title.
+ * <p>
+ * In order for the progress bar to be shown, the feature must be requested
+ * via {@link #requestWindowFeature(int)}.
+ *
+ * @param progress The progress for the progress bar. Valid ranges are from
+ * 0 to 10000 (both inclusive). If 10000 is given, the progress
+ * bar will be completely filled and will fade out.
+ */
+ public final void setProgress(int progress) {
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress + Window.PROGRESS_START);
+ }
+
+ /**
+ * Sets the secondary progress for the progress bar in the title. This
+ * progress is drawn between the primary progress (set via
+ * {@link #setProgress(int)} and the background. It can be ideal for media
+ * scenarios such as showing the buffering progress while the default
+ * progress shows the play progress.
+ * <p>
+ * In order for the progress bar to be shown, the feature must be requested
+ * via {@link #requestWindowFeature(int)}.
+ *
+ * @param secondaryProgress The secondary progress for the progress bar. Valid ranges are from
+ * 0 to 10000 (both inclusive).
+ */
+ public final void setSecondaryProgress(int secondaryProgress) {
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
+ secondaryProgress + Window.PROGRESS_SECONDARY_START);
+ }
+
+ /**
+ * Suggests an audio stream whose volume should be changed by the hardware
+ * volume controls.
+ * <p>
+ * The suggested audio stream will be tied to the window of this Activity.
+ * If the Activity is switched, the stream set here is no longer the
+ * suggested stream. The client does not need to save and restore the old
+ * suggested stream value in onPause and onResume.
+ *
+ * @param streamType The type of the audio stream whose volume should be
+ * changed by the hardware volume controls. It is not guaranteed that
+ * the hardware volume controls will always change this stream's
+ * volume (for example, if a call is in progress, its stream's volume
+ * may be changed instead). To reset back to the default, use
+ * {@link AudioManager#USE_DEFAULT_STREAM_TYPE}.
+ */
+ public final void setVolumeControlStream(int streamType) {
+ getWindow().setVolumeControlStream(streamType);
+ }
+
+ /**
+ * Gets the suggested audio stream whose volume should be changed by the
+ * harwdare volume controls.
+ *
+ * @return The suggested audio stream type whose volume should be changed by
+ * the hardware volume controls.
+ * @see #setVolumeControlStream(int)
+ */
+ public final int getVolumeControlStream() {
+ return getWindow().getVolumeControlStream();
+ }
+
+ /**
+ * Runs the specified action on the UI thread. If the current thread is the UI
+ * thread, then the action is executed immediately. If the current thread is
+ * not the UI thread, the action is posted to the event queue of the UI thread.
+ *
+ * @param action the action to run on the UI thread
+ */
+ public final void runOnUiThread(Runnable action) {
+ if (Thread.currentThread() != mUiThread) {
+ mHandler.post(action);
+ } else {
+ action.run();
+ }
+ }
+
+ /**
+ * Stub implementation of {@link android.view.LayoutInflater.Factory#onCreateView} used when
+ * inflating with the LayoutInflater returned by {@link #getSystemService}. This
+ * implementation simply returns null for all view names.
+ *
+ * @see android.view.LayoutInflater#createView
+ * @see android.view.Window#getLayoutInflater
+ */
+ public View onCreateView(String name, Context context, AttributeSet attrs) {
+ return null;
+ }
+
+ // ------------------ Internal API ------------------
+
+ final void setParent(Activity parent) {
+ mParent = parent;
+ }
+
+ final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
+ Application application, Intent intent, ActivityInfo info, CharSequence title,
+ Activity parent, String id, Object lastNonConfigurationInstance,
+ Configuration config) {
+ attach(context, aThread, instr, token, application, intent, info, title, parent, id,
+ lastNonConfigurationInstance, null, config);
+ }
+
+ final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
+ Application application, Intent intent, ActivityInfo info, CharSequence title,
+ Activity parent, String id, Object lastNonConfigurationInstance,
+ HashMap<String,Object> lastNonConfigurationChildInstances, Configuration config) {
+ attachBaseContext(context);
+
+ mWindow = PolicyManager.makeNewWindow(this);
+ mWindow.setCallback(this);
+ if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
+ mWindow.setSoftInputMode(info.softInputMode);
+ }
+ mUiThread = Thread.currentThread();
+
+ mMainThread = aThread;
+ mInstrumentation = instr;
+ mToken = token;
+ mApplication = application;
+ mIntent = intent;
+ mComponent = intent.getComponent();
+ mActivityInfo = info;
+ mTitle = title;
+ mParent = parent;
+ mEmbeddedID = id;
+ mLastNonConfigurationInstance = lastNonConfigurationInstance;
+ mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;
+
+ mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
+ if (mParent != null) {
+ mWindow.setContainer(mParent.getWindow());
+ }
+ mWindowManager = mWindow.getWindowManager();
+ mCurrentConfig = config;
+ }
+
+ final IBinder getActivityToken() {
+ return mParent != null ? mParent.getActivityToken() : mToken;
+ }
+
+ final void performStart() {
+ mCalled = false;
+ mInstrumentation.callActivityOnStart(this);
+ if (!mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + mComponent.toShortString() +
+ " did not call through to super.onStart()");
+ }
+ }
+
+ final void performRestart() {
+ final int N = mManagedCursors.size();
+ for (int i=0; i<N; i++) {
+ ManagedCursor mc = mManagedCursors.get(i);
+ if (mc.mReleased || mc.mUpdated) {
+ mc.mCursor.requery();
+ mc.mReleased = false;
+ mc.mUpdated = false;
+ }
+ }
+
+ if (mStopped) {
+ mStopped = false;
+ mCalled = false;
+ mInstrumentation.callActivityOnRestart(this);
+ if (!mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + mComponent.toShortString() +
+ " did not call through to super.onRestart()");
+ }
+ performStart();
+ }
+ }
+
+ final void performResume() {
+ performRestart();
+
+ mLastNonConfigurationInstance = null;
+
+ // First call onResume() -before- setting mResumed, so we don't
+ // send out any status bar / menu notifications the client makes.
+ mCalled = false;
+ mInstrumentation.callActivityOnResume(this);
+ if (!mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + mComponent.toShortString() +
+ " did not call through to super.onResume()");
+ }
+
+ // Now really resume, and install the current status bar and menu.
+ mResumed = true;
+ mCalled = false;
+ onPostResume();
+ if (!mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + mComponent.toShortString() +
+ " did not call through to super.onPostResume()");
+ }
+ }
+
+ final void performPause() {
+ onPause();
+ }
+
+ final void performUserLeaving() {
+ onUserInteraction();
+ onUserLeaveHint();
+ }
+
+ final void performStop() {
+ if (!mStopped) {
+ if (mWindow != null) {
+ mWindow.closeAllPanels();
+ }
+
+ mCalled = false;
+ mInstrumentation.callActivityOnStop(this);
+ if (!mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + mComponent.toShortString() +
+ " did not call through to super.onStop()");
+ }
+
+ final int N = mManagedCursors.size();
+ for (int i=0; i<N; i++) {
+ ManagedCursor mc = mManagedCursors.get(i);
+ if (!mc.mReleased) {
+ mc.mCursor.deactivate();
+ mc.mReleased = true;
+ }
+ }
+
+ mStopped = true;
+ }
+ mResumed = false;
+ }
+
+ final boolean isResumed() {
+ return mResumed;
+ }
+
+ void dispatchActivityResult(String who, int requestCode,
+ int resultCode, Intent data) {
+ if (Config.LOGV) Log.v(
+ TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
+ + ", resCode=" + resultCode + ", data=" + data);
+ if (who == null) {
+ onActivityResult(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/core/java/android/app/ActivityGroup.java b/core/java/android/app/ActivityGroup.java
new file mode 100644
index 0000000..f1216f9
--- /dev/null
+++ b/core/java/android/app/ActivityGroup.java
@@ -0,0 +1,127 @@
+/*
+ * 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.app;
+
+import java.util.HashMap;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * A screen that contains and runs multiple embedded activities.
+ */
+public class ActivityGroup extends Activity {
+ private static final String TAG = "ActivityGroup";
+ private static final String STATES_KEY = "android:states";
+ static final String PARENT_NON_CONFIG_INSTANCE_KEY = "android:parent_non_config_instance";
+
+ /**
+ * This field should be made private, so it is hidden from the SDK.
+ * {@hide}
+ */
+ protected LocalActivityManager mLocalActivityManager;
+
+ public ActivityGroup() {
+ this(true);
+ }
+
+ public ActivityGroup(boolean singleActivityMode) {
+ mLocalActivityManager = new LocalActivityManager(this, singleActivityMode);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Bundle states = savedInstanceState != null
+ ? (Bundle) savedInstanceState.getBundle(STATES_KEY) : null;
+ mLocalActivityManager.dispatchCreate(states);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mLocalActivityManager.dispatchResume();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ Bundle state = mLocalActivityManager.saveInstanceState();
+ if (state != null) {
+ outState.putBundle(STATES_KEY, state);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mLocalActivityManager.dispatchPause(isFinishing());
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mLocalActivityManager.dispatchStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mLocalActivityManager.dispatchDestroy(isFinishing());
+ }
+
+ /**
+ * Returns a HashMap mapping from child activity ids to the return values
+ * from calls to their onRetainNonConfigurationInstance methods.
+ *
+ * {@hide}
+ */
+ @Override
+ public HashMap<String,Object> onRetainNonConfigurationChildInstances() {
+ return mLocalActivityManager.dispatchRetainNonConfigurationInstance();
+ }
+
+ public Activity getCurrentActivity() {
+ return mLocalActivityManager.getCurrentActivity();
+ }
+
+ public final LocalActivityManager getLocalActivityManager() {
+ return mLocalActivityManager;
+ }
+
+ @Override
+ void dispatchActivityResult(String who, int requestCode, int resultCode,
+ Intent data) {
+ if (who != null) {
+ Activity act = mLocalActivityManager.getActivity(who);
+ /*
+ if (Config.LOGV) Log.v(
+ TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
+ + ", resCode=" + resultCode + ", data=" + data
+ + ", rec=" + rec);
+ */
+ if (act != null) {
+ act.onActivityResult(requestCode, resultCode, data);
+ return;
+ }
+ }
+ super.dispatchActivityResult(who, requestCode, resultCode, data);
+ }
+}
+
+
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
new file mode 100644
index 0000000..07520c9d
--- /dev/null
+++ b/core/java/android/app/ActivityManager.java
@@ -0,0 +1,761 @@
+/*
+ * 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.app;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.graphics.Bitmap;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import java.util.List;
+
+/**
+ * Interact with the overall activities running in the system.
+ */
+public class ActivityManager {
+ private static String TAG = "ActivityManager";
+ private static boolean DEBUG = false;
+ private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+
+ private final Context mContext;
+ private final Handler mHandler;
+
+ /*package*/ ActivityManager(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ }
+
+ /**
+ * Information you can retrieve about tasks that the user has most recently
+ * started or visited.
+ */
+ public static class RecentTaskInfo implements Parcelable {
+ /**
+ * If this task is currently running, this is the identifier for it.
+ * If it is not running, this will be -1.
+ */
+ public int id;
+
+ /**
+ * The original Intent used to launch the task. You can use this
+ * Intent to re-launch the task (if it is no longer running) or bring
+ * the current task to the front.
+ */
+ public Intent baseIntent;
+
+ /**
+ * If this task was started from an alias, this is the actual
+ * activity component that was initially started; the component of
+ * the baseIntent in this case is the name of the actual activity
+ * implementation that the alias referred to. Otherwise, this is null.
+ */
+ public ComponentName origActivity;
+
+ public RecentTaskInfo() {
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(id);
+ if (baseIntent != null) {
+ dest.writeInt(1);
+ baseIntent.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
+ }
+ ComponentName.writeToParcel(origActivity, dest);
+ }
+
+ public void readFromParcel(Parcel source) {
+ id = source.readInt();
+ if (source.readInt() != 0) {
+ baseIntent = Intent.CREATOR.createFromParcel(source);
+ } else {
+ baseIntent = null;
+ }
+ origActivity = ComponentName.readFromParcel(source);
+ }
+
+ public static final Creator<RecentTaskInfo> CREATOR
+ = new Creator<RecentTaskInfo>() {
+ public RecentTaskInfo createFromParcel(Parcel source) {
+ return new RecentTaskInfo(source);
+ }
+ public RecentTaskInfo[] newArray(int size) {
+ return new RecentTaskInfo[size];
+ }
+ };
+
+ private RecentTaskInfo(Parcel source) {
+ readFromParcel(source);
+ }
+ }
+
+ /**
+ * Flag for use with {@link #getRecentTasks}: return all tasks, even those
+ * that have set their
+ * {@link android.content.Intent#FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS} flag.
+ */
+ public static final int RECENT_WITH_EXCLUDED = 0x0001;
+
+ /**
+ * Return a list of the tasks that the user has recently launched, with
+ * the most recent being first and older ones after in order.
+ *
+ * @param maxNum The maximum number of entries to return in the list. The
+ * actual number returned may be smaller, depending on how many tasks the
+ * user has started and the maximum number the system can remember.
+ *
+ * @return Returns a list of RecentTaskInfo records describing each of
+ * the recent tasks.
+ *
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not hold the {@link android.Manifest.permission#GET_TASKS} permission.
+ */
+ public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags)
+ throws SecurityException {
+ try {
+ return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
+ flags);
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ return null;
+ }
+ }
+
+ /**
+ * Information you can retrieve about a particular task that is currently
+ * "running" in the system. Note that a running task does not mean the
+ * given task actual has a process it is actively running in; it simply
+ * means that the user has gone to it and never closed it, but currently
+ * the system may have killed its process and is only holding on to its
+ * last state in order to restart it when the user returns.
+ */
+ public static class RunningTaskInfo implements Parcelable {
+ /**
+ * A unique identifier for this task.
+ */
+ public int id;
+
+ /**
+ * The component launched as the first activity in the task. This can
+ * be considered the "application" of this task.
+ */
+ public ComponentName baseActivity;
+
+ /**
+ * The activity component at the top of the history stack of the task.
+ * This is what the user is currently doing.
+ */
+ public ComponentName topActivity;
+
+ /**
+ * Thumbnail representation of the task's current state.
+ */
+ public Bitmap thumbnail;
+
+ /**
+ * Description of the task's current state.
+ */
+ public CharSequence description;
+
+ /**
+ * Number of activities in this task.
+ */
+ public int numActivities;
+
+ /**
+ * Number of activities that are currently running (not stopped
+ * and persisted) in this task.
+ */
+ public int numRunning;
+
+ public RunningTaskInfo() {
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(id);
+ ComponentName.writeToParcel(baseActivity, dest);
+ ComponentName.writeToParcel(topActivity, dest);
+ if (thumbnail != null) {
+ dest.writeInt(1);
+ thumbnail.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
+ }
+ TextUtils.writeToParcel(description, dest,
+ Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ dest.writeInt(numActivities);
+ dest.writeInt(numRunning);
+ }
+
+ public void readFromParcel(Parcel source) {
+ id = source.readInt();
+ baseActivity = ComponentName.readFromParcel(source);
+ topActivity = ComponentName.readFromParcel(source);
+ if (source.readInt() != 0) {
+ thumbnail = Bitmap.CREATOR.createFromParcel(source);
+ } else {
+ thumbnail = null;
+ }
+ description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ numActivities = source.readInt();
+ numRunning = source.readInt();
+ }
+
+ public static final Creator<RunningTaskInfo> CREATOR = new Creator<RunningTaskInfo>() {
+ public RunningTaskInfo createFromParcel(Parcel source) {
+ return new RunningTaskInfo(source);
+ }
+ public RunningTaskInfo[] newArray(int size) {
+ return new RunningTaskInfo[size];
+ }
+ };
+
+ private RunningTaskInfo(Parcel source) {
+ readFromParcel(source);
+ }
+ }
+
+ /**
+ * Return a list of the tasks that are currently running, with
+ * the most recent being first and older ones after in order. Note that
+ * "running" does not mean any of the task's code is currently loaded or
+ * activity -- the task may have been frozen by the system, so that it
+ * can be restarted in its previous state when next brought to the
+ * foreground.
+ *
+ * @param maxNum The maximum number of entries to return in the list. The
+ * actual number returned may be smaller, depending on how many tasks the
+ * user has started.
+ *
+ * @return Returns a list of RunningTaskInfo records describing each of
+ * the running tasks.
+ *
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not hold the {@link android.Manifest.permission#GET_TASKS} permission.
+ */
+ public List<RunningTaskInfo> getRunningTasks(int maxNum)
+ throws SecurityException {
+ try {
+ return (List<RunningTaskInfo>)ActivityManagerNative.getDefault()
+ .getTasks(maxNum, 0, null);
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ return null;
+ }
+ }
+
+ /**
+ * Information you can retrieve about a particular Service that is
+ * currently running in the system.
+ */
+ public static class RunningServiceInfo implements Parcelable {
+ /**
+ * The service component.
+ */
+ public ComponentName service;
+
+ /**
+ * If non-zero, this is the process the service is running in.
+ */
+ public int pid;
+
+ /**
+ * The name of the process this service runs in.
+ */
+ public String process;
+
+ /**
+ * Set to true if the service has asked to run as a foreground process.
+ */
+ public boolean foreground;
+
+ /**
+ * The time when the service was first made activity, either by someone
+ * starting or binding to it.
+ */
+ public long activeSince;
+
+ /**
+ * Set to true if this service has been explicitly started.
+ */
+ public boolean started;
+
+ /**
+ * Number of clients connected to the service.
+ */
+ public int clientCount;
+
+ /**
+ * Number of times the service's process has crashed while the service
+ * is running.
+ */
+ public int crashCount;
+
+ /**
+ * The time when there was last activity in the service (either
+ * explicit requests to start it or clients binding to it).
+ */
+ public long lastActivityTime;
+
+ /**
+ * If non-zero, this service is not currently running, but scheduled to
+ * restart at the given time.
+ */
+ public long restarting;
+
+ public RunningServiceInfo() {
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ ComponentName.writeToParcel(service, dest);
+ dest.writeInt(pid);
+ dest.writeString(process);
+ dest.writeInt(foreground ? 1 : 0);
+ dest.writeLong(activeSince);
+ dest.writeInt(started ? 1 : 0);
+ dest.writeInt(clientCount);
+ dest.writeInt(crashCount);
+ dest.writeLong(lastActivityTime);
+ dest.writeLong(restarting);
+ }
+
+ public void readFromParcel(Parcel source) {
+ service = ComponentName.readFromParcel(source);
+ pid = source.readInt();
+ process = source.readString();
+ foreground = source.readInt() != 0;
+ activeSince = source.readLong();
+ started = source.readInt() != 0;
+ clientCount = source.readInt();
+ crashCount = source.readInt();
+ lastActivityTime = source.readLong();
+ restarting = source.readLong();
+ }
+
+ public static final Creator<RunningServiceInfo> CREATOR = new Creator<RunningServiceInfo>() {
+ public RunningServiceInfo createFromParcel(Parcel source) {
+ return new RunningServiceInfo(source);
+ }
+ public RunningServiceInfo[] newArray(int size) {
+ return new RunningServiceInfo[size];
+ }
+ };
+
+ private RunningServiceInfo(Parcel source) {
+ readFromParcel(source);
+ }
+ }
+
+ /**
+ * Return a list of the services that are currently running.
+ *
+ * @param maxNum The maximum number of entries to return in the list. The
+ * actual number returned may be smaller, depending on how many services
+ * are running.
+ *
+ * @return Returns a list of RunningServiceInfo records describing each of
+ * the running tasks.
+ */
+ public List<RunningServiceInfo> getRunningServices(int maxNum)
+ throws SecurityException {
+ try {
+ return (List<RunningServiceInfo>)ActivityManagerNative.getDefault()
+ .getServices(maxNum, 0);
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ return null;
+ }
+ }
+
+ /**
+ * Information you can retrieve about the available memory through
+ * {@link ActivityManager#getMemoryInfo}.
+ */
+ public static class MemoryInfo implements Parcelable {
+ /**
+ * The total available memory on the system. This number should not
+ * be considered absolute: due to the nature of the kernel, a significant
+ * portion of this memory is actually in use and needed for the overall
+ * system to run well.
+ */
+ public long availMem;
+
+ /**
+ * The threshold of {@link #availMem} at which we consider memory to be
+ * low and start killing background services and other non-extraneous
+ * processes.
+ */
+ public long threshold;
+
+ /**
+ * Set to true if the system considers itself to currently be in a low
+ * memory situation.
+ */
+ public boolean lowMemory;
+
+ public MemoryInfo() {
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(availMem);
+ dest.writeLong(threshold);
+ dest.writeInt(lowMemory ? 1 : 0);
+ }
+
+ public void readFromParcel(Parcel source) {
+ availMem = source.readLong();
+ threshold = source.readLong();
+ lowMemory = source.readInt() != 0;
+ }
+
+ public static final Creator<MemoryInfo> CREATOR
+ = new Creator<MemoryInfo>() {
+ public MemoryInfo createFromParcel(Parcel source) {
+ return new MemoryInfo(source);
+ }
+ public MemoryInfo[] newArray(int size) {
+ return new MemoryInfo[size];
+ }
+ };
+
+ private MemoryInfo(Parcel source) {
+ readFromParcel(source);
+ }
+ }
+
+ public void getMemoryInfo(MemoryInfo outInfo) {
+ try {
+ ActivityManagerNative.getDefault().getMemoryInfo(outInfo);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
+ try {
+ return ActivityManagerNative.getDefault().clearApplicationUserData(packageName,
+ observer);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Information you can retrieve about any processes that are in an error condition.
+ */
+ public static class ProcessErrorStateInfo implements Parcelable {
+ /**
+ * Condition codes
+ */
+ public static final int NO_ERROR = 0;
+ public static final int CRASHED = 1;
+ public static final int NOT_RESPONDING = 2;
+
+ /**
+ * The condition that the process is in.
+ */
+ public int condition;
+
+ /**
+ * The process name in which the crash or error occurred.
+ */
+ public String processName;
+
+ /**
+ * The pid of this process; 0 if none
+ */
+ public int pid;
+
+ /**
+ * The kernel user-ID that has been assigned to this process;
+ * currently this is not a unique ID (multiple applications can have
+ * the same uid).
+ */
+ public int uid;
+
+ /**
+ * The tag that was provided when the process crashed.
+ */
+ public String tag;
+
+ /**
+ * A short message describing the error condition.
+ */
+ public String shortMsg;
+
+ /**
+ * A long message describing the error condition.
+ */
+ public String longMsg;
+
+ /**
+ * Raw data about the crash (typically a stack trace).
+ */
+ public byte[] crashData;
+
+ public ProcessErrorStateInfo() {
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(condition);
+ dest.writeString(processName);
+ dest.writeInt(pid);
+ dest.writeInt(uid);
+ dest.writeString(tag);
+ dest.writeString(shortMsg);
+ dest.writeString(longMsg);
+ dest.writeInt(crashData == null ? -1 : crashData.length);
+ dest.writeByteArray(crashData);
+ }
+
+ public void readFromParcel(Parcel source) {
+ condition = source.readInt();
+ processName = source.readString();
+ pid = source.readInt();
+ uid = source.readInt();
+ tag = source.readString();
+ shortMsg = source.readString();
+ longMsg = source.readString();
+ int cdLen = source.readInt();
+ if (cdLen == -1) {
+ crashData = null;
+ } else {
+ crashData = new byte[cdLen];
+ source.readByteArray(crashData);
+ }
+ }
+
+ public static final Creator<ProcessErrorStateInfo> CREATOR =
+ new Creator<ProcessErrorStateInfo>() {
+ public ProcessErrorStateInfo createFromParcel(Parcel source) {
+ return new ProcessErrorStateInfo(source);
+ }
+ public ProcessErrorStateInfo[] newArray(int size) {
+ return new ProcessErrorStateInfo[size];
+ }
+ };
+
+ private ProcessErrorStateInfo(Parcel source) {
+ readFromParcel(source);
+ }
+ }
+
+ /**
+ * Returns a list of any processes that are currently in an error condition. The result
+ * will be null if all processes are running properly at this time.
+ *
+ * @return Returns a list of ProcessErrorStateInfo records, or null if there are no
+ * current error conditions (it will not return an empty list). This list ordering is not
+ * specified.
+ */
+ public List<ProcessErrorStateInfo> getProcessesInErrorState() {
+ try {
+ return ActivityManagerNative.getDefault().getProcessesInErrorState();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Information you can retrieve about a running process.
+ */
+ public static class RunningAppProcessInfo implements Parcelable {
+ /**
+ * The name of the process that this object is associated with
+ */
+ public String processName;
+
+ /**
+ * The pid of this process; 0 if none
+ */
+ public int pid;
+
+ public String pkgList[];
+
+ /**
+ * Constant for {@link #importance}: this process is running the
+ * foreground UI.
+ */
+ public static final int IMPORTANCE_FOREGROUND = 100;
+
+ /**
+ * Constant for {@link #importance}: this process is running something
+ * that is considered to be actively visible to the user.
+ */
+ public static final int IMPORTANCE_VISIBLE = 200;
+
+ /**
+ * Constant for {@link #importance}: this process is contains services
+ * that should remain running.
+ */
+ public static final int IMPORTANCE_SERVICE = 300;
+
+ /**
+ * Constant for {@link #importance}: this process process contains
+ * background code that is expendable.
+ */
+ public static final int IMPORTANCE_BACKGROUND = 400;
+
+ /**
+ * Constant for {@link #importance}: this process is empty of any
+ * actively running code.
+ */
+ public static final int IMPORTANCE_EMPTY = 500;
+
+ /**
+ * The relative importance level that the system places on this
+ * process. May be one of {@link #IMPORTANCE_FOREGROUND},
+ * {@link #IMPORTANCE_VISIBLE}, {@link #IMPORTANCE_SERVICE},
+ * {@link #IMPORTANCE_BACKGROUND}, or {@link #IMPORTANCE_EMPTY}. These
+ * constants are numbered so that "more important" values are always
+ * smaller than "less important" values.
+ */
+ public int importance;
+
+ /**
+ * An additional ordering within a particular {@link #importance}
+ * category, providing finer-grained information about the relative
+ * utility of processes within a category. This number means nothing
+ * except that a smaller values are more recently used (and thus
+ * more important). Currently an LRU value is only maintained for
+ * the {@link #IMPORTANCE_BACKGROUND} category, though others may
+ * be maintained in the future.
+ */
+ public int lru;
+
+ public RunningAppProcessInfo() {
+ importance = IMPORTANCE_FOREGROUND;
+ }
+
+ public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) {
+ processName = pProcessName;
+ pid = pPid;
+ pkgList = pArr;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(processName);
+ dest.writeInt(pid);
+ dest.writeStringArray(pkgList);
+ dest.writeInt(importance);
+ dest.writeInt(lru);
+ }
+
+ public void readFromParcel(Parcel source) {
+ processName = source.readString();
+ pid = source.readInt();
+ pkgList = source.readStringArray();
+ importance = source.readInt();
+ lru = source.readInt();
+ }
+
+ public static final Creator<RunningAppProcessInfo> CREATOR =
+ new Creator<RunningAppProcessInfo>() {
+ public RunningAppProcessInfo createFromParcel(Parcel source) {
+ return new RunningAppProcessInfo(source);
+ }
+ public RunningAppProcessInfo[] newArray(int size) {
+ return new RunningAppProcessInfo[size];
+ }
+ };
+
+ private RunningAppProcessInfo(Parcel source) {
+ readFromParcel(source);
+ }
+ }
+
+ /**
+ * Returns a list of application processes that are running on the device.
+ *
+ * @return Returns a list of RunningAppProcessInfo records, or null if there are no
+ * running processes (it will not return an empty list). This list ordering is not
+ * specified.
+ */
+ public List<RunningAppProcessInfo> getRunningAppProcesses() {
+ try {
+ return ActivityManagerNative.getDefault().getRunningAppProcesses();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Have the system perform a force stop of everything associated with
+ * the given application package. All processes that share its uid
+ * will be killed, all services it has running stopped, all activities
+ * removed, etc. In addition, a {@link Intent#ACTION_PACKAGE_RESTARTED}
+ * broadcast will be sent, so that any of its registered alarms can
+ * be stopped, notifications removed, etc.
+ *
+ * <p>You must hold the permission
+ * {@link android.Manifest.permission#RESTART_PACKAGES} to be able to
+ * call this method.
+ *
+ * @param packageName The name of the package to be stopped.
+ */
+ public void restartPackage(String packageName) {
+ try {
+ ActivityManagerNative.getDefault().restartPackage(packageName);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Get the device configuration attributes.
+ */
+ public ConfigurationInfo getDeviceConfigurationInfo() {
+ try {
+ return ActivityManagerNative.getDefault().getDeviceConfigurationInfo();
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
new file mode 100644
index 0000000..f11dbec
--- /dev/null
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -0,0 +1,2135 @@
+/*
+ * 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.app;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+import android.util.Config;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/** {@hide} */
+public abstract class ActivityManagerNative extends Binder implements IActivityManager
+{
+ /**
+ * Cast a Binder object into an activity manager interface, generating
+ * a proxy if needed.
+ */
+ static public IActivityManager asInterface(IBinder obj)
+ {
+ if (obj == null) {
+ return null;
+ }
+ IActivityManager in =
+ (IActivityManager)obj.queryLocalInterface(descriptor);
+ if (in != null) {
+ return in;
+ }
+
+ return new ActivityManagerProxy(obj);
+ }
+
+ /**
+ * Retrieve the system's default/global activity manager.
+ */
+ static public IActivityManager getDefault()
+ {
+ if (gDefault != null) {
+ //if (Config.LOGV) Log.v(
+ // "ActivityManager", "returning cur default = " + gDefault);
+ return gDefault;
+ }
+ IBinder b = ServiceManager.getService("activity");
+ if (Config.LOGV) Log.v(
+ "ActivityManager", "default service binder = " + b);
+ gDefault = asInterface(b);
+ if (Config.LOGV) Log.v(
+ "ActivityManager", "default service = " + gDefault);
+ return gDefault;
+ }
+
+ /**
+ * Convenience for checking whether the system is ready. For internal use only.
+ */
+ static public boolean isSystemReady() {
+ if (!sSystemReady) {
+ sSystemReady = getDefault().testIsSystemReady();
+ }
+ return sSystemReady;
+ }
+ static boolean sSystemReady = false;
+
+ /**
+ * Convenience for sending a sticky broadcast. For internal use only.
+ * If you don't care about permission, use null.
+ */
+ static public void broadcastStickyIntent(Intent intent, String permission)
+ {
+ try {
+ getDefault().broadcastIntent(
+ null, intent, null, null, Activity.RESULT_OK, null, null,
+ null /*permission*/, false, true);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ static public void noteWakeupAlarm(PendingIntent ps) {
+ try {
+ getDefault().noteWakeupAlarm(ps.getTarget());
+ } catch (RemoteException ex) {
+ }
+ }
+
+ public ActivityManagerNative()
+ {
+ attachInterface(this, descriptor);
+ }
+
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ switch (code) {
+ case START_ACTIVITY_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ Uri[] grantedUriPermissions = data.createTypedArray(Uri.CREATOR);
+ int grantedMode = data.readInt();
+ IBinder resultTo = data.readStrongBinder();
+ String resultWho = data.readString();
+ int requestCode = data.readInt();
+ boolean onlyIfNeeded = data.readInt() != 0;
+ boolean debug = data.readInt() != 0;
+ int result = startActivity(app, intent, resolvedType,
+ grantedUriPermissions, grantedMode, resultTo, resultWho,
+ requestCode, onlyIfNeeded, debug);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
+ case START_NEXT_MATCHING_ACTIVITY_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder callingActivity = data.readStrongBinder();
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ boolean result = startNextMatchingActivity(callingActivity, intent);
+ reply.writeNoException();
+ reply.writeInt(result ? 1 : 0);
+ return true;
+ }
+
+ case FINISH_ACTIVITY_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ Intent resultData = null;
+ int resultCode = data.readInt();
+ if (data.readInt() != 0) {
+ resultData = Intent.CREATOR.createFromParcel(data);
+ }
+ boolean res = finishActivity(token, resultCode, resultData);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
+ case FINISH_SUB_ACTIVITY_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ String resultWho = data.readString();
+ int requestCode = data.readInt();
+ finishSubActivity(token, resultWho, requestCode);
+ reply.writeNoException();
+ return true;
+ }
+
+ case REGISTER_RECEIVER_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app =
+ b != null ? ApplicationThreadNative.asInterface(b) : null;
+ b = data.readStrongBinder();
+ IIntentReceiver rec
+ = b != null ? IIntentReceiver.Stub.asInterface(b) : null;
+ IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data);
+ String perm = data.readString();
+ Intent intent = registerReceiver(app, rec, filter, perm);
+ reply.writeNoException();
+ if (intent != null) {
+ reply.writeInt(1);
+ intent.writeToParcel(reply, 0);
+ } else {
+ reply.writeInt(0);
+ }
+ return true;
+ }
+
+ case UNREGISTER_RECEIVER_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ if (b == null) {
+ return true;
+ }
+ IIntentReceiver rec = IIntentReceiver.Stub.asInterface(b);
+ unregisterReceiver(rec);
+ reply.writeNoException();
+ return true;
+ }
+
+ case BROADCAST_INTENT_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app =
+ b != null ? ApplicationThreadNative.asInterface(b) : null;
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ b = data.readStrongBinder();
+ IIntentReceiver resultTo =
+ b != null ? IIntentReceiver.Stub.asInterface(b) : null;
+ int resultCode = data.readInt();
+ String resultData = data.readString();
+ Bundle resultExtras = data.readBundle();
+ String perm = data.readString();
+ boolean serialized = data.readInt() != 0;
+ boolean sticky = data.readInt() != 0;
+ int res = broadcastIntent(app, intent, resolvedType, resultTo,
+ resultCode, resultData, resultExtras, perm,
+ serialized, sticky);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
+ case UNBROADCAST_INTENT_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = b != null ? ApplicationThreadNative.asInterface(b) : null;
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ unbroadcastIntent(app, intent);
+ reply.writeNoException();
+ return true;
+ }
+
+ case FINISH_RECEIVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder who = data.readStrongBinder();
+ int resultCode = data.readInt();
+ String resultData = data.readString();
+ Bundle resultExtras = data.readBundle();
+ boolean resultAbort = data.readInt() != 0;
+ if (who != null) {
+ finishReceiver(who, resultCode, resultData, resultExtras, resultAbort);
+ }
+ reply.writeNoException();
+ return true;
+ }
+
+ case SET_PERSISTENT_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ boolean isPersistent = data.readInt() != 0;
+ if (token != null) {
+ setPersistent(token, isPersistent);
+ }
+ reply.writeNoException();
+ return true;
+ }
+
+ case ATTACH_APPLICATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IApplicationThread app = ApplicationThreadNative.asInterface(
+ data.readStrongBinder());
+ if (app != null) {
+ attachApplication(app);
+ }
+ reply.writeNoException();
+ return true;
+ }
+
+ case ACTIVITY_IDLE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ if (token != null) {
+ activityIdle(token);
+ }
+ reply.writeNoException();
+ return true;
+ }
+
+ case ACTIVITY_PAUSED_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ Bundle map = data.readBundle();
+ activityPaused(token, map);
+ reply.writeNoException();
+ return true;
+ }
+
+ case ACTIVITY_STOPPED_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ Bitmap thumbnail = data.readInt() != 0
+ ? Bitmap.CREATOR.createFromParcel(data) : null;
+ CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
+ activityStopped(token, thumbnail, description);
+ reply.writeNoException();
+ return true;
+ }
+
+ case ACTIVITY_DESTROYED_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ activityDestroyed(token);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_CALLING_PACKAGE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ String res = token != null ? getCallingPackage(token) : null;
+ reply.writeNoException();
+ reply.writeString(res);
+ return true;
+ }
+
+ case GET_CALLING_ACTIVITY_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ ComponentName cn = getCallingActivity(token);
+ reply.writeNoException();
+ ComponentName.writeToParcel(cn, reply);
+ return true;
+ }
+
+ case GET_TASKS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int maxNum = data.readInt();
+ int fl = data.readInt();
+ IBinder receiverBinder = data.readStrongBinder();
+ IThumbnailReceiver receiver = receiverBinder != null
+ ? IThumbnailReceiver.Stub.asInterface(receiverBinder)
+ : null;
+ List list = getTasks(maxNum, fl, receiver);
+ reply.writeNoException();
+ int N = list != null ? list.size() : -1;
+ reply.writeInt(N);
+ int i;
+ for (i=0; i<N; i++) {
+ ActivityManager.RunningTaskInfo info =
+ (ActivityManager.RunningTaskInfo)list.get(i);
+ info.writeToParcel(reply, 0);
+ }
+ return true;
+ }
+
+ case GET_RECENT_TASKS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int maxNum = data.readInt();
+ int fl = data.readInt();
+ List<ActivityManager.RecentTaskInfo> list = getRecentTasks(maxNum,
+ fl);
+ reply.writeNoException();
+ reply.writeTypedList(list);
+ return true;
+ }
+
+ case GET_SERVICES_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int maxNum = data.readInt();
+ int fl = data.readInt();
+ List list = getServices(maxNum, fl);
+ reply.writeNoException();
+ int N = list != null ? list.size() : -1;
+ reply.writeInt(N);
+ int i;
+ for (i=0; i<N; i++) {
+ ActivityManager.RunningServiceInfo info =
+ (ActivityManager.RunningServiceInfo)list.get(i);
+ info.writeToParcel(reply, 0);
+ }
+ return true;
+ }
+
+ case GET_PROCESSES_IN_ERROR_STATE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ List<ActivityManager.ProcessErrorStateInfo> list = getProcessesInErrorState();
+ reply.writeNoException();
+ reply.writeTypedList(list);
+ return true;
+ }
+
+ case GET_RUNNING_APP_PROCESSES_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ List<ActivityManager.RunningAppProcessInfo> list = getRunningAppProcesses();
+ reply.writeNoException();
+ reply.writeTypedList(list);
+ return true;
+ }
+
+ case MOVE_TASK_TO_FRONT_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int task = data.readInt();
+ moveTaskToFront(task);
+ reply.writeNoException();
+ return true;
+ }
+
+ case MOVE_TASK_TO_BACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int task = data.readInt();
+ moveTaskToBack(task);
+ reply.writeNoException();
+ return true;
+ }
+
+ case MOVE_ACTIVITY_TASK_TO_BACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ boolean nonRoot = data.readInt() != 0;
+ boolean res = moveActivityTaskToBack(token, nonRoot);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
+ case MOVE_TASK_BACKWARDS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int task = data.readInt();
+ moveTaskBackwards(task);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_TASK_FOR_ACTIVITY_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ boolean onlyRoot = data.readInt() != 0;
+ int res = token != null
+ ? getTaskForActivity(token, onlyRoot) : -1;
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
+ case FINISH_OTHER_INSTANCES_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ ComponentName className = ComponentName.readFromParcel(data);
+ finishOtherInstances(token, className);
+ reply.writeNoException();
+ return true;
+ }
+
+ case REPORT_THUMBNAIL_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ Bitmap thumbnail = data.readInt() != 0
+ ? Bitmap.CREATOR.createFromParcel(data) : null;
+ CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
+ reportThumbnail(token, thumbnail, description);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_CONTENT_PROVIDER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ String name = data.readString();
+ ContentProviderHolder cph = getContentProvider(app, name);
+ reply.writeNoException();
+ if (cph != null) {
+ reply.writeInt(1);
+ cph.writeToParcel(reply, 0);
+ } else {
+ reply.writeInt(0);
+ }
+ return true;
+ }
+
+ case PUBLISH_CONTENT_PROVIDERS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ ArrayList<ContentProviderHolder> providers =
+ data.createTypedArrayList(ContentProviderHolder.CREATOR);
+ publishContentProviders(app, providers);
+ reply.writeNoException();
+ return true;
+ }
+
+ case REMOVE_CONTENT_PROVIDER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ String name = data.readString();
+ removeContentProvider(app, name);
+ reply.writeNoException();
+ return true;
+ }
+
+ case START_SERVICE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ Intent service = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ ComponentName cn = startService(app, service, resolvedType);
+ reply.writeNoException();
+ ComponentName.writeToParcel(cn, reply);
+ return true;
+ }
+
+ case STOP_SERVICE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ Intent service = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ int res = stopService(app, service, resolvedType);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
+ case STOP_SERVICE_TOKEN_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ ComponentName className = ComponentName.readFromParcel(data);
+ IBinder token = data.readStrongBinder();
+ int startId = data.readInt();
+ boolean res = stopServiceToken(className, token, startId);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
+ case SET_SERVICE_FOREGROUND_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ ComponentName className = ComponentName.readFromParcel(data);
+ IBinder token = data.readStrongBinder();
+ boolean isForeground = data.readInt() != 0;
+ setServiceForeground(className, token, isForeground);
+ reply.writeNoException();
+ return true;
+ }
+
+ case BIND_SERVICE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IBinder token = data.readStrongBinder();
+ Intent service = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ b = data.readStrongBinder();
+ int fl = data.readInt();
+ IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
+ int res = bindService(app, token, service, resolvedType, conn, fl);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
+ case UNBIND_SERVICE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
+ boolean res = unbindService(conn);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
+ case PUBLISH_SERVICE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ IBinder service = data.readStrongBinder();
+ publishService(token, intent, service);
+ reply.writeNoException();
+ return true;
+ }
+
+ case UNBIND_FINISHED_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ boolean doRebind = data.readInt() != 0;
+ unbindFinished(token, intent, doRebind);
+ reply.writeNoException();
+ return true;
+ }
+
+ case SERVICE_DONE_EXECUTING_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ serviceDoneExecuting(token);
+ reply.writeNoException();
+ return true;
+ }
+
+ case START_INSTRUMENTATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ ComponentName className = ComponentName.readFromParcel(data);
+ String profileFile = data.readString();
+ int fl = data.readInt();
+ Bundle arguments = data.readBundle();
+ IBinder b = data.readStrongBinder();
+ IInstrumentationWatcher w = IInstrumentationWatcher.Stub.asInterface(b);
+ boolean res = startInstrumentation(className, profileFile, fl, arguments, w);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
+
+ case FINISH_INSTRUMENTATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ int resultCode = data.readInt();
+ Bundle results = data.readBundle();
+ finishInstrumentation(app, resultCode, results);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_CONFIGURATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ Configuration config = getConfiguration();
+ reply.writeNoException();
+ config.writeToParcel(reply, 0);
+ return true;
+ }
+
+ case UPDATE_CONFIGURATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ Configuration config = Configuration.CREATOR.createFromParcel(data);
+ updateConfiguration(config);
+ reply.writeNoException();
+ return true;
+ }
+
+ case SET_REQUESTED_ORIENTATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ int requestedOrientation = data.readInt();
+ setRequestedOrientation(token, requestedOrientation);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_REQUESTED_ORIENTATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ int req = getRequestedOrientation(token);
+ reply.writeNoException();
+ reply.writeInt(req);
+ return true;
+ }
+
+ case GET_ACTIVITY_CLASS_FOR_TOKEN_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ ComponentName cn = getActivityClassForToken(token);
+ reply.writeNoException();
+ ComponentName.writeToParcel(cn, reply);
+ return true;
+ }
+
+ case GET_PACKAGE_FOR_TOKEN_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ reply.writeNoException();
+ reply.writeString(getPackageForToken(token));
+ return true;
+ }
+
+ case GET_INTENT_SENDER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int type = data.readInt();
+ String packageName = data.readString();
+ IBinder token = data.readStrongBinder();
+ String resultWho = data.readString();
+ int requestCode = data.readInt();
+ Intent requestIntent = data.readInt() != 0
+ ? Intent.CREATOR.createFromParcel(data) : null;
+ String requestResolvedType = data.readString();
+ int fl = data.readInt();
+ IIntentSender res = getIntentSender(type, packageName, token,
+ resultWho, requestCode, requestIntent,
+ requestResolvedType, fl);
+ reply.writeNoException();
+ reply.writeStrongBinder(res != null ? res.asBinder() : null);
+ return true;
+ }
+
+ case CANCEL_INTENT_SENDER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IIntentSender r = IIntentSender.Stub.asInterface(
+ data.readStrongBinder());
+ cancelIntentSender(r);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_PACKAGE_FOR_INTENT_SENDER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IIntentSender r = IIntentSender.Stub.asInterface(
+ data.readStrongBinder());
+ String res = getPackageForIntentSender(r);
+ reply.writeNoException();
+ reply.writeString(res);
+ return true;
+ }
+
+ case SET_PROCESS_LIMIT_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int max = data.readInt();
+ setProcessLimit(max);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_PROCESS_LIMIT_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int limit = getProcessLimit();
+ reply.writeNoException();
+ reply.writeInt(limit);
+ return true;
+ }
+
+ case SET_PROCESS_FOREGROUND_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ int pid = data.readInt();
+ boolean isForeground = data.readInt() != 0;
+ setProcessForeground(token, pid, isForeground);
+ reply.writeNoException();
+ return true;
+ }
+
+ case CHECK_PERMISSION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String perm = data.readString();
+ int pid = data.readInt();
+ int uid = data.readInt();
+ int res = checkPermission(perm, pid, uid);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
+ case CHECK_URI_PERMISSION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ int pid = data.readInt();
+ int uid = data.readInt();
+ int mode = data.readInt();
+ int res = checkUriPermission(uri, pid, uid, mode);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
+ case CLEAR_APP_DATA_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String packageName = data.readString();
+ IPackageDataObserver observer = IPackageDataObserver.Stub.asInterface(
+ data.readStrongBinder());
+ boolean res = clearApplicationUserData(packageName, observer);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
+ case GRANT_URI_PERMISSION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ String targetPkg = data.readString();
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ int mode = data.readInt();
+ grantUriPermission(app, targetPkg, uri, mode);
+ reply.writeNoException();
+ return true;
+ }
+
+ case REVOKE_URI_PERMISSION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ int mode = data.readInt();
+ revokeUriPermission(app, uri, mode);
+ reply.writeNoException();
+ return true;
+ }
+
+ case SHOW_WAITING_FOR_DEBUGGER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ boolean waiting = data.readInt() != 0;
+ showWaitingForDebugger(app, waiting);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_MEMORY_INFO_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
+ getMemoryInfo(mi);
+ reply.writeNoException();
+ mi.writeToParcel(reply, 0);
+ return true;
+ }
+
+ case UNHANDLED_BACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ unhandledBack();
+ reply.writeNoException();
+ return true;
+ }
+
+ case OPEN_CONTENT_URI_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ Uri uri = Uri.parse(data.readString());
+ ParcelFileDescriptor pfd = openContentUri(uri);
+ reply.writeNoException();
+ if (pfd != null) {
+ reply.writeInt(1);
+ pfd.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ } else {
+ reply.writeInt(0);
+ }
+ return true;
+ }
+
+ case GOING_TO_SLEEP_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ goingToSleep();
+ reply.writeNoException();
+ return true;
+ }
+
+ case WAKING_UP_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ wakingUp();
+ reply.writeNoException();
+ return true;
+ }
+
+ case SET_DEBUG_APP_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String pn = data.readString();
+ boolean wfd = data.readInt() != 0;
+ boolean per = data.readInt() != 0;
+ setDebugApp(pn, wfd, per);
+ reply.writeNoException();
+ return true;
+ }
+
+ case SET_ALWAYS_FINISH_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ boolean enabled = data.readInt() != 0;
+ setAlwaysFinish(enabled);
+ reply.writeNoException();
+ return true;
+ }
+
+ case SET_ACTIVITY_WATCHER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IActivityWatcher watcher = IActivityWatcher.Stub.asInterface(
+ data.readStrongBinder());
+ setActivityWatcher(watcher);
+ return true;
+ }
+
+ case ENTER_SAFE_MODE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ enterSafeMode();
+ reply.writeNoException();
+ return true;
+ }
+
+ case NOTE_WAKEUP_ALARM_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IIntentSender is = IIntentSender.Stub.asInterface(
+ data.readStrongBinder());
+ noteWakeupAlarm(is);
+ reply.writeNoException();
+ return true;
+ }
+
+ case KILL_PIDS_FOR_MEMORY_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int[] pids = data.createIntArray();
+ boolean res = killPidsForMemory(pids);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
+ case REPORT_PSS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ int pss = data.readInt();
+ reportPss(app, pss);
+ reply.writeNoException();
+ return true;
+ }
+
+ case START_RUNNING_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String pkg = data.readString();
+ String cls = data.readString();
+ String action = data.readString();
+ String indata = data.readString();
+ startRunning(pkg, cls, action, indata);
+ reply.writeNoException();
+ return true;
+ }
+
+ case SYSTEM_READY_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ systemReady();
+ reply.writeNoException();
+ return true;
+ }
+
+ case HANDLE_APPLICATION_ERROR_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder app = data.readStrongBinder();
+ int fl = data.readInt();
+ String tag = data.readString();
+ String shortMsg = data.readString();
+ String longMsg = data.readString();
+ byte[] crashData = data.createByteArray();
+ int res = handleApplicationError(app, fl, tag, shortMsg, longMsg,
+ crashData);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
+ case SIGNAL_PERSISTENT_PROCESSES_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int sig = data.readInt();
+ signalPersistentProcesses(sig);
+ reply.writeNoException();
+ return true;
+ }
+
+ case RESTART_PACKAGE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String packageName = data.readString();
+ restartPackage(packageName);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_DEVICE_CONFIGURATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ ConfigurationInfo config = getDeviceConfigurationInfo();
+ reply.writeNoException();
+ config.writeToParcel(reply, 0);
+ return true;
+ }
+
+ case PEEK_SERVICE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ Intent service = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ IBinder binder = peekService(service, resolvedType);
+ reply.writeNoException();
+ reply.writeStrongBinder(binder);
+ return true;
+ }
+ }
+
+ return super.onTransact(code, data, reply, flags);
+ }
+
+ public IBinder asBinder()
+ {
+ return this;
+ }
+
+ private static IActivityManager gDefault;
+}
+
+class ActivityManagerProxy implements IActivityManager
+{
+ public ActivityManagerProxy(IBinder remote)
+ {
+ mRemote = remote;
+ }
+
+ public IBinder asBinder()
+ {
+ return mRemote;
+ }
+
+ public int startActivity(IApplicationThread caller, Intent intent,
+ String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
+ IBinder resultTo, String resultWho,
+ int requestCode, boolean onlyIfNeeded,
+ boolean debug) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ intent.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ data.writeTypedArray(grantedUriPermissions, 0);
+ data.writeInt(grantedMode);
+ data.writeStrongBinder(resultTo);
+ data.writeString(resultWho);
+ data.writeInt(requestCode);
+ data.writeInt(onlyIfNeeded ? 1 : 0);
+ data.writeInt(debug ? 1 : 0);
+ mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+ public boolean startNextMatchingActivity(IBinder callingActivity,
+ Intent intent) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(callingActivity);
+ intent.writeToParcel(data, 0);
+ mRemote.transact(START_NEXT_MATCHING_ACTIVITY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result != 0;
+ }
+ public boolean finishActivity(IBinder token, int resultCode, Intent resultData)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(resultCode);
+ if (resultData != null) {
+ data.writeInt(1);
+ resultData.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ mRemote.transact(FINISH_ACTIVITY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ data.writeString(resultWho);
+ data.writeInt(requestCode);
+ mRemote.transact(FINISH_SUB_ACTIVITY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public Intent registerReceiver(IApplicationThread caller,
+ IIntentReceiver receiver,
+ IntentFilter filter, String perm) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
+ filter.writeToParcel(data, 0);
+ data.writeString(perm);
+ mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ Intent intent = null;
+ int haveIntent = reply.readInt();
+ if (haveIntent != 0) {
+ intent = Intent.CREATOR.createFromParcel(reply);
+ }
+ reply.recycle();
+ data.recycle();
+ return intent;
+ }
+ public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(receiver.asBinder());
+ mRemote.transact(UNREGISTER_RECEIVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public int broadcastIntent(IApplicationThread caller,
+ Intent intent, String resolvedType, IIntentReceiver resultTo,
+ int resultCode, String resultData, Bundle map,
+ String requiredPermission, boolean serialized,
+ boolean sticky) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ intent.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ data.writeStrongBinder(resultTo != null ? resultTo.asBinder() : null);
+ data.writeInt(resultCode);
+ data.writeString(resultData);
+ data.writeBundle(map);
+ data.writeString(requiredPermission);
+ data.writeInt(serialized ? 1 : 0);
+ data.writeInt(sticky ? 1 : 0);
+ mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return res;
+ }
+ public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ intent.writeToParcel(data, 0);
+ mRemote.transact(UNBROADCAST_INTENT_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(who);
+ data.writeInt(resultCode);
+ data.writeString(resultData);
+ data.writeBundle(map);
+ data.writeInt(abortBroadcast ? 1 : 0);
+ mRemote.transact(FINISH_RECEIVER_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void setPersistent(IBinder token, boolean isPersistent) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(isPersistent ? 1 : 0);
+ mRemote.transact(SET_PERSISTENT_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void attachApplication(IApplicationThread app) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(app.asBinder());
+ mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void activityIdle(IBinder token) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(ACTIVITY_IDLE_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void activityPaused(IBinder token, Bundle state) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ data.writeBundle(state);
+ mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void activityStopped(IBinder token,
+ Bitmap thumbnail, CharSequence description) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ if (thumbnail != null) {
+ data.writeInt(1);
+ thumbnail.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ TextUtils.writeToParcel(description, data, 0);
+ mRemote.transact(ACTIVITY_STOPPED_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void activityDestroyed(IBinder token) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(ACTIVITY_DESTROYED_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public String getCallingPackage(IBinder token) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(GET_CALLING_PACKAGE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ String res = reply.readString();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public ComponentName getCallingActivity(IBinder token)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(GET_CALLING_ACTIVITY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ComponentName res = ComponentName.readFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public List getTasks(int maxNum, int flags,
+ IThumbnailReceiver receiver) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(maxNum);
+ data.writeInt(flags);
+ data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
+ mRemote.transact(GET_TASKS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ArrayList list = null;
+ int N = reply.readInt();
+ if (N >= 0) {
+ list = new ArrayList();
+ while (N > 0) {
+ ActivityManager.RunningTaskInfo info =
+ ActivityManager.RunningTaskInfo.CREATOR
+ .createFromParcel(reply);
+ list.add(info);
+ N--;
+ }
+ }
+ data.recycle();
+ reply.recycle();
+ return list;
+ }
+ public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
+ int flags) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(maxNum);
+ data.writeInt(flags);
+ mRemote.transact(GET_RECENT_TASKS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ArrayList<ActivityManager.RecentTaskInfo> list
+ = reply.createTypedArrayList(ActivityManager.RecentTaskInfo.CREATOR);
+ data.recycle();
+ reply.recycle();
+ return list;
+ }
+ public List getServices(int maxNum, int flags) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(maxNum);
+ data.writeInt(flags);
+ mRemote.transact(GET_SERVICES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ArrayList list = null;
+ int N = reply.readInt();
+ if (N >= 0) {
+ list = new ArrayList();
+ while (N > 0) {
+ ActivityManager.RunningServiceInfo info =
+ ActivityManager.RunningServiceInfo.CREATOR
+ .createFromParcel(reply);
+ list.add(info);
+ N--;
+ }
+ }
+ data.recycle();
+ reply.recycle();
+ return list;
+ }
+ public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_PROCESSES_IN_ERROR_STATE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ArrayList<ActivityManager.ProcessErrorStateInfo> list
+ = reply.createTypedArrayList(ActivityManager.ProcessErrorStateInfo.CREATOR);
+ data.recycle();
+ reply.recycle();
+ return list;
+ }
+ public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses()
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_RUNNING_APP_PROCESSES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ArrayList<ActivityManager.RunningAppProcessInfo> list
+ = reply.createTypedArrayList(ActivityManager.RunningAppProcessInfo.CREATOR);
+ data.recycle();
+ reply.recycle();
+ return list;
+ }
+ public void moveTaskToFront(int task) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(task);
+ mRemote.transact(MOVE_TASK_TO_FRONT_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void moveTaskToBack(int task) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(task);
+ mRemote.transact(MOVE_TASK_TO_BACK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(nonRoot ? 1 : 0);
+ mRemote.transact(MOVE_ACTIVITY_TASK_TO_BACK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public void moveTaskBackwards(int task) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(task);
+ mRemote.transact(MOVE_TASK_BACKWARDS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(onlyRoot ? 1 : 0);
+ mRemote.transact(GET_TASK_FOR_ACTIVITY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public void finishOtherInstances(IBinder token, ComponentName className) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ ComponentName.writeToParcel(className, data);
+ mRemote.transact(FINISH_OTHER_INSTANCES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void reportThumbnail(IBinder token,
+ Bitmap thumbnail, CharSequence description) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ if (thumbnail != null) {
+ data.writeInt(1);
+ thumbnail.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ TextUtils.writeToParcel(description, data, 0);
+ mRemote.transact(REPORT_THUMBNAIL_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public ContentProviderHolder getContentProvider(IApplicationThread caller,
+ String name) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ data.writeString(name);
+ mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ ContentProviderHolder cph = null;
+ if (res != 0) {
+ cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
+ }
+ data.recycle();
+ reply.recycle();
+ return cph;
+ }
+ public void publishContentProviders(IApplicationThread caller,
+ List<ContentProviderHolder> providers) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ data.writeTypedList(providers);
+ mRemote.transact(PUBLISH_CONTENT_PROVIDERS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void removeContentProvider(IApplicationThread caller,
+ String name) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ data.writeString(name);
+ mRemote.transact(REMOVE_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public ComponentName startService(IApplicationThread caller, Intent service,
+ String resolvedType) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ service.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ComponentName res = ComponentName.readFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public int stopService(IApplicationThread caller, Intent service,
+ String resolvedType) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ service.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ mRemote.transact(STOP_SERVICE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return res;
+ }
+ public boolean stopServiceToken(ComponentName className, IBinder token,
+ int startId) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ ComponentName.writeToParcel(className, data);
+ data.writeStrongBinder(token);
+ data.writeInt(startId);
+ mRemote.transact(STOP_SERVICE_TOKEN_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public void setServiceForeground(ComponentName className, IBinder token,
+ boolean isForeground) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ ComponentName.writeToParcel(className, data);
+ data.writeStrongBinder(token);
+ data.writeInt(isForeground ? 1 : 0);
+ mRemote.transact(SET_SERVICE_FOREGROUND_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public int bindService(IApplicationThread caller, IBinder token,
+ Intent service, String resolvedType, IServiceConnection connection,
+ int flags) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ data.writeStrongBinder(token);
+ service.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ data.writeStrongBinder(connection.asBinder());
+ data.writeInt(flags);
+ mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public boolean unbindService(IServiceConnection connection) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(connection.asBinder());
+ mRemote.transact(UNBIND_SERVICE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+
+ public void publishService(IBinder token,
+ Intent intent, IBinder service) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ intent.writeToParcel(data, 0);
+ data.writeStrongBinder(service);
+ mRemote.transact(PUBLISH_SERVICE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void unbindFinished(IBinder token, Intent intent, boolean doRebind)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ intent.writeToParcel(data, 0);
+ data.writeInt(doRebind ? 1 : 0);
+ mRemote.transact(UNBIND_FINISHED_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void serviceDoneExecuting(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(SERVICE_DONE_EXECUTING_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public IBinder peekService(Intent service, String resolvedType) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ service.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ mRemote.transact(PEEK_SERVICE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ IBinder binder = reply.readStrongBinder();
+ reply.recycle();
+ data.recycle();
+ return binder;
+ }
+
+ public boolean startInstrumentation(ComponentName className, String profileFile,
+ int flags, Bundle arguments, IInstrumentationWatcher watcher)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ ComponentName.writeToParcel(className, data);
+ data.writeString(profileFile);
+ data.writeInt(flags);
+ data.writeBundle(arguments);
+ data.writeStrongBinder(watcher != null ? watcher.asBinder() : null);
+ mRemote.transact(START_INSTRUMENTATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ reply.recycle();
+ data.recycle();
+ return res;
+ }
+
+ public void finishInstrumentation(IApplicationThread target,
+ int resultCode, Bundle results) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(target != null ? target.asBinder() : null);
+ data.writeInt(resultCode);
+ data.writeBundle(results);
+ mRemote.transact(FINISH_INSTRUMENTATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public Configuration getConfiguration() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_CONFIGURATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ Configuration res = Configuration.CREATOR.createFromParcel(reply);
+ reply.recycle();
+ data.recycle();
+ return res;
+ }
+ public void updateConfiguration(Configuration values) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ values.writeToParcel(data, 0);
+ mRemote.transact(UPDATE_CONFIGURATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void setRequestedOrientation(IBinder token, int requestedOrientation)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(requestedOrientation);
+ mRemote.transact(SET_REQUESTED_ORIENTATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public int getRequestedOrientation(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(GET_REQUESTED_ORIENTATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public ComponentName getActivityClassForToken(IBinder token)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(GET_ACTIVITY_CLASS_FOR_TOKEN_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ComponentName res = ComponentName.readFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public String getPackageForToken(IBinder token) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(GET_PACKAGE_FOR_TOKEN_TRANSACTION, data, reply, 0);
+ reply.readException();
+ String res = reply.readString();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public IIntentSender getIntentSender(int type,
+ String packageName, IBinder token, String resultWho,
+ int requestCode, Intent intent, String resolvedType, int flags)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(type);
+ data.writeString(packageName);
+ data.writeStrongBinder(token);
+ data.writeString(resultWho);
+ data.writeInt(requestCode);
+ if (intent != null) {
+ data.writeInt(1);
+ intent.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ data.writeString(resolvedType);
+ data.writeInt(flags);
+ mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ IIntentSender res = IIntentSender.Stub.asInterface(
+ reply.readStrongBinder());
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public void cancelIntentSender(IIntentSender sender) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(sender.asBinder());
+ mRemote.transact(CANCEL_INTENT_SENDER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public String getPackageForIntentSender(IIntentSender sender) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(sender.asBinder());
+ mRemote.transact(GET_PACKAGE_FOR_INTENT_SENDER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ String res = reply.readString();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public void setProcessLimit(int max) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(max);
+ mRemote.transact(SET_PROCESS_LIMIT_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public int getProcessLimit() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_PROCESS_LIMIT_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public void setProcessForeground(IBinder token, int pid,
+ boolean isForeground) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(pid);
+ data.writeInt(isForeground ? 1 : 0);
+ mRemote.transact(SET_PROCESS_FOREGROUND_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public int checkPermission(String permission, int pid, int uid)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(permission);
+ data.writeInt(pid);
+ data.writeInt(uid);
+ mRemote.transact(CHECK_PERMISSION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public boolean clearApplicationUserData(final String packageName,
+ final IPackageDataObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(packageName);
+ data.writeStrongBinder(observer.asBinder());
+ mRemote.transact(CLEAR_APP_DATA_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public int checkUriPermission(Uri uri, int pid, int uid, int mode)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ uri.writeToParcel(data, 0);
+ data.writeInt(pid);
+ data.writeInt(uid);
+ data.writeInt(mode);
+ mRemote.transact(CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public void grantUriPermission(IApplicationThread caller, String targetPkg,
+ Uri uri, int mode) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller.asBinder());
+ data.writeString(targetPkg);
+ uri.writeToParcel(data, 0);
+ data.writeInt(mode);
+ mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void revokeUriPermission(IApplicationThread caller, Uri uri,
+ int mode) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller.asBinder());
+ uri.writeToParcel(data, 0);
+ data.writeInt(mode);
+ mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(who.asBinder());
+ data.writeInt(waiting ? 1 : 0);
+ mRemote.transact(SHOW_WAITING_FOR_DEBUGGER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_MEMORY_INFO_TRANSACTION, data, reply, 0);
+ reply.readException();
+ outInfo.readFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ }
+ public void unhandledBack() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(UNHANDLED_BACK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(OPEN_CONTENT_URI_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ParcelFileDescriptor pfd = null;
+ if (reply.readInt() != 0) {
+ pfd = ParcelFileDescriptor.CREATOR.createFromParcel(reply);
+ }
+ data.recycle();
+ reply.recycle();
+ return pfd;
+ }
+ public void goingToSleep() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GOING_TO_SLEEP_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void wakingUp() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(WAKING_UP_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void setDebugApp(
+ String packageName, boolean waitForDebugger, boolean persistent)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(packageName);
+ data.writeInt(waitForDebugger ? 1 : 0);
+ data.writeInt(persistent ? 1 : 0);
+ mRemote.transact(SET_DEBUG_APP_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void setAlwaysFinish(boolean enabled) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(enabled ? 1 : 0);
+ mRemote.transact(SET_ALWAYS_FINISH_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void setActivityWatcher(IActivityWatcher watcher) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(watcher != null ? watcher.asBinder() : null);
+ mRemote.transact(SET_ACTIVITY_WATCHER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void enterSafeMode() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(ENTER_SAFE_MODE_TRANSACTION, data, null, 0);
+ data.recycle();
+ }
+ public void noteWakeupAlarm(IIntentSender sender) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeStrongBinder(sender.asBinder());
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(NOTE_WAKEUP_ALARM_TRANSACTION, data, null, 0);
+ data.recycle();
+ }
+ public boolean killPidsForMemory(int[] pids) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeIntArray(pids);
+ mRemote.transact(KILL_PIDS_FOR_MEMORY_TRANSACTION, data, reply, 0);
+ boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public void reportPss(IApplicationThread caller, int pss) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller.asBinder());
+ data.writeInt(pss);
+ mRemote.transact(REPORT_PSS_TRANSACTION, data, null, 0);
+ data.recycle();
+ }
+ public void startRunning(String pkg, String cls, String action,
+ String indata) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(pkg);
+ data.writeString(cls);
+ data.writeString(action);
+ data.writeString(indata);
+ mRemote.transact(START_RUNNING_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void systemReady() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(SYSTEM_READY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public boolean testIsSystemReady()
+ {
+ /* this base class version is never called */
+ return true;
+ }
+ public int handleApplicationError(IBinder app, int flags,
+ String tag, String shortMsg, String longMsg,
+ byte[] crashData) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(app);
+ data.writeInt(flags);
+ data.writeString(tag);
+ data.writeString(shortMsg);
+ data.writeString(longMsg);
+ data.writeByteArray(crashData);
+ mRemote.transact(HANDLE_APPLICATION_ERROR_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return res;
+ }
+
+ public void signalPersistentProcesses(int sig) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(sig);
+ mRemote.transact(SIGNAL_PERSISTENT_PROCESSES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void restartPackage(String packageName) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(packageName);
+ mRemote.transact(RESTART_PACKAGE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_DEVICE_CONFIGURATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ConfigurationInfo res = ConfigurationInfo.CREATOR.createFromParcel(reply);
+ reply.recycle();
+ data.recycle();
+ return res;
+ }
+ private IBinder mRemote;
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
new file mode 100644
index 0000000..bf5616e
--- /dev/null
+++ b/core/java/android/app/ActivityThread.java
@@ -0,0 +1,3916 @@
+/*
+ * 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.app;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentCallbacks;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDebug;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.net.http.AndroidHttpClient;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.AndroidRuntimeException;
+import android.util.Config;
+import android.util.DisplayMetrics;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.Display;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewManager;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+
+import com.android.internal.os.BinderInternal;
+import com.android.internal.os.RuntimeInit;
+import com.android.internal.util.ArrayUtils;
+
+import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.regex.Pattern;
+
+final class IntentReceiverLeaked extends AndroidRuntimeException {
+ public IntentReceiverLeaked(String msg) {
+ super(msg);
+ }
+}
+
+final class ServiceConnectionLeaked extends AndroidRuntimeException {
+ public ServiceConnectionLeaked(String msg) {
+ super(msg);
+ }
+}
+
+final class SuperNotCalledException extends AndroidRuntimeException {
+ public SuperNotCalledException(String msg) {
+ super(msg);
+ }
+}
+
+/**
+ * This manages the execution of the main thread in an
+ * application process, scheduling and executing activities,
+ * broadcasts, and other operations on it as the activity
+ * manager requests.
+ *
+ * {@hide}
+ */
+public final class ActivityThread {
+ private static final String TAG = "ActivityThread";
+ private static final boolean DEBUG = false;
+ private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean DEBUG_BROADCAST = false;
+ private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
+ private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
+ private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
+ private static final int LOG_ON_PAUSE_CALLED = 30021;
+ private static final int LOG_ON_RESUME_CALLED = 30022;
+
+
+ public static final ActivityThread currentActivityThread() {
+ return (ActivityThread)sThreadLocal.get();
+ }
+
+ public static final String currentPackageName()
+ {
+ ActivityThread am = currentActivityThread();
+ return (am != null && am.mBoundApplication != null)
+ ? am.mBoundApplication.processName : null;
+ }
+
+ public static IPackageManager getPackageManager() {
+ if (sPackageManager != null) {
+ //Log.v("PackageManager", "returning cur default = " + sPackageManager);
+ return sPackageManager;
+ }
+ IBinder b = ServiceManager.getService("package");
+ //Log.v("PackageManager", "default service binder = " + b);
+ sPackageManager = IPackageManager.Stub.asInterface(b);
+ //Log.v("PackageManager", "default service = " + sPackageManager);
+ return sPackageManager;
+ }
+
+ DisplayMetrics getDisplayMetricsLocked(boolean forceUpdate) {
+ if (mDisplayMetrics != null && !forceUpdate) {
+ return mDisplayMetrics;
+ }
+ if (mDisplay == null) {
+ WindowManager wm = WindowManagerImpl.getDefault();
+ mDisplay = wm.getDefaultDisplay();
+ }
+ DisplayMetrics metrics = mDisplayMetrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ //Log.i("foo", "New metrics: w=" + metrics.widthPixels + " h="
+ // + metrics.heightPixels + " den=" + metrics.density
+ // + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi);
+ return metrics;
+ }
+
+ Resources getTopLevelResources(String appDir) {
+ synchronized (mPackages) {
+ //Log.w(TAG, "getTopLevelResources: " + appDir);
+ WeakReference<Resources> wr = mActiveResources.get(appDir);
+ Resources r = wr != null ? wr.get() : null;
+ if (r != null && r.getAssets().isUpToDate()) {
+ //Log.w(TAG, "Returning cached resources " + r + " " + appDir);
+ return r;
+ }
+
+ //if (r != null) {
+ // Log.w(TAG, "Throwing away out-of-date resources!!!! "
+ // + r + " " + appDir);
+ //}
+
+ AssetManager assets = new AssetManager();
+ if (assets.addAssetPath(appDir) == 0) {
+ return null;
+ }
+ DisplayMetrics metrics = getDisplayMetricsLocked(false);
+ r = new Resources(assets, metrics, getConfiguration());
+ //Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration());
+ // XXX need to remove entries when weak references go away
+ mActiveResources.put(appDir, new WeakReference<Resources>(r));
+ return r;
+ }
+ }
+
+ final Handler getHandler() {
+ return mH;
+ }
+
+ public final static class PackageInfo {
+
+ private final ActivityThread mActivityThread;
+ private final ApplicationInfo mApplicationInfo;
+ private final String mPackageName;
+ private final String mAppDir;
+ private final String mResDir;
+ private final String[] mSharedLibraries;
+ private final String mDataDir;
+ private final File mDataDirFile;
+ private final ClassLoader mBaseClassLoader;
+ private final boolean mSecurityViolation;
+ private final boolean mIncludeCode;
+ private Resources mResources;
+ private ClassLoader mClassLoader;
+ private Application mApplication;
+ private final HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
+ = new HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>>();
+ private final HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>> mUnregisteredReceivers
+ = new HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>>();
+ private final HashMap<Context, HashMap<ServiceConnection, ServiceDispatcher>> mServices
+ = new HashMap<Context, HashMap<ServiceConnection, ServiceDispatcher>>();
+ private final HashMap<Context, HashMap<ServiceConnection, ServiceDispatcher>> mUnboundServices
+ = new HashMap<Context, HashMap<ServiceConnection, ServiceDispatcher>>();
+
+ int mClientCount = 0;
+
+ public PackageInfo(ActivityThread activityThread, ApplicationInfo aInfo,
+ ActivityThread mainThread, ClassLoader baseLoader,
+ boolean securityViolation, boolean includeCode) {
+ mActivityThread = activityThread;
+ mApplicationInfo = aInfo;
+ mPackageName = aInfo.packageName;
+ mAppDir = aInfo.sourceDir;
+ mResDir = aInfo.uid == Process.myUid() ? aInfo.sourceDir
+ : aInfo.publicSourceDir;
+ mSharedLibraries = aInfo.sharedLibraryFiles;
+ mDataDir = aInfo.dataDir;
+ mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
+ mBaseClassLoader = baseLoader;
+ mSecurityViolation = securityViolation;
+ mIncludeCode = includeCode;
+
+ if (mAppDir == null) {
+ if (mSystemContext == null) {
+ mSystemContext =
+ ApplicationContext.createSystemContext(mainThread);
+ mSystemContext.getResources().updateConfiguration(
+ mainThread.getConfiguration(),
+ mainThread.getDisplayMetricsLocked(false));
+ //Log.i(TAG, "Created system resources "
+ // + mSystemContext.getResources() + ": "
+ // + mSystemContext.getResources().getConfiguration());
+ }
+ mClassLoader = mSystemContext.getClassLoader();
+ mResources = mSystemContext.getResources();
+ }
+ }
+
+ public PackageInfo(ActivityThread activityThread, String name,
+ Context systemContext) {
+ mActivityThread = activityThread;
+ mApplicationInfo = new ApplicationInfo();
+ mApplicationInfo.packageName = name;
+ mPackageName = name;
+ mAppDir = null;
+ mResDir = null;
+ mSharedLibraries = null;
+ mDataDir = null;
+ mDataDirFile = null;
+ mBaseClassLoader = null;
+ mSecurityViolation = false;
+ mIncludeCode = true;
+ mClassLoader = systemContext.getClassLoader();
+ mResources = systemContext.getResources();
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public boolean isSecurityViolation() {
+ return mSecurityViolation;
+ }
+
+ /**
+ * Gets the array of shared libraries that are listed as
+ * used by the given package.
+ *
+ * @param packageName the name of the package (note: not its
+ * file name)
+ * @return null-ok; the array of shared libraries, each one
+ * a fully-qualified path
+ */
+ private static String[] getLibrariesFor(String packageName) {
+ ApplicationInfo ai = null;
+ try {
+ ai = getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_SHARED_LIBRARY_FILES);
+ } catch (RemoteException e) {
+ throw new AssertionError(e);
+ }
+
+ if (ai == null) {
+ return null;
+ }
+
+ return ai.sharedLibraryFiles;
+ }
+
+ /**
+ * Combines two arrays (of library names) such that they are
+ * concatenated in order but are devoid of duplicates. The
+ * result is a single string with the names of the libraries
+ * separated by colons, or <code>null</code> if both lists
+ * were <code>null</code> or empty.
+ *
+ * @param list1 null-ok; the first list
+ * @param list2 null-ok; the second list
+ * @return null-ok; the combination
+ */
+ private static String combineLibs(String[] list1, String[] list2) {
+ StringBuilder result = new StringBuilder(300);
+ boolean first = true;
+
+ if (list1 != null) {
+ for (String s : list1) {
+ if (first) {
+ first = false;
+ } else {
+ result.append(':');
+ }
+ result.append(s);
+ }
+ }
+
+ // Only need to check for duplicates if list1 was non-empty.
+ boolean dupCheck = !first;
+
+ if (list2 != null) {
+ for (String s : list2) {
+ if (dupCheck && ArrayUtils.contains(list1, s)) {
+ continue;
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ result.append(':');
+ }
+ result.append(s);
+ }
+ }
+
+ return result.toString();
+ }
+
+ public ClassLoader getClassLoader() {
+ synchronized (this) {
+ if (mClassLoader != null) {
+ return mClassLoader;
+ }
+
+ if (mIncludeCode && !mPackageName.equals("android")) {
+ String zip = mAppDir;
+
+ /*
+ * The following is a bit of a hack to inject
+ * instrumentation into the system: If the app
+ * being started matches one of the instrumentation names,
+ * then we combine both the "instrumentation" and
+ * "instrumented" app into the path, along with the
+ * concatenation of both apps' shared library lists.
+ */
+
+ String instrumentationAppDir =
+ mActivityThread.mInstrumentationAppDir;
+ String instrumentationAppPackage =
+ mActivityThread.mInstrumentationAppPackage;
+ String instrumentedAppDir =
+ mActivityThread.mInstrumentedAppDir;
+ String[] instrumentationLibs = null;
+
+ if (mAppDir.equals(instrumentationAppDir)
+ || mAppDir.equals(instrumentedAppDir)) {
+ zip = instrumentationAppDir + ":" + instrumentedAppDir;
+ if (! instrumentedAppDir.equals(instrumentationAppDir)) {
+ instrumentationLibs =
+ getLibrariesFor(instrumentationAppPackage);
+ }
+ }
+
+ if ((mSharedLibraries != null) ||
+ (instrumentationLibs != null)) {
+ zip =
+ combineLibs(mSharedLibraries, instrumentationLibs)
+ + ':' + zip;
+ }
+
+ /*
+ * With all the combination done (if necessary, actually
+ * create the class loader.
+ */
+
+ if (localLOGV) Log.v(TAG, "Class path: " + zip);
+
+ mClassLoader =
+ ApplicationLoaders.getDefault().getClassLoader(
+ zip, mDataDir, mBaseClassLoader);
+ } else {
+ if (mBaseClassLoader == null) {
+ mClassLoader = ClassLoader.getSystemClassLoader();
+ } else {
+ mClassLoader = mBaseClassLoader;
+ }
+ }
+ return mClassLoader;
+ }
+ }
+
+ public String getAppDir() {
+ return mAppDir;
+ }
+
+ public String getResDir() {
+ return mResDir;
+ }
+
+ public String getDataDir() {
+ return mDataDir;
+ }
+
+ public File getDataDirFile() {
+ return mDataDirFile;
+ }
+
+ public AssetManager getAssets(ActivityThread mainThread) {
+ return getResources(mainThread).getAssets();
+ }
+
+ public Resources getResources(ActivityThread mainThread) {
+ if (mResources == null) {
+ mResources = mainThread.getTopLevelResources(mResDir);
+ }
+ return mResources;
+ }
+
+ public Application makeApplication() {
+ if (mApplication != null) {
+ return mApplication;
+ }
+
+ Application app = null;
+
+ String appClass = mApplicationInfo.className;
+ if (appClass == null) {
+ appClass = "android.app.Application";
+ }
+
+ try {
+ java.lang.ClassLoader cl = getClassLoader();
+ ApplicationContext appContext = new ApplicationContext();
+ appContext.init(this, null, mActivityThread);
+ app = mActivityThread.mInstrumentation.newApplication(
+ cl, appClass, appContext);
+ appContext.setOuterContext(app);
+ } catch (Exception e) {
+ if (!mActivityThread.mInstrumentation.onException(app, e)) {
+ throw new RuntimeException(
+ "Unable to instantiate application " + appClass
+ + ": " + e.toString(), e);
+ }
+ }
+ mActivityThread.mAllApplications.add(app);
+ return mApplication = app;
+ }
+
+ public void removeContextRegistrations(Context context,
+ String who, String what) {
+ HashMap<BroadcastReceiver, ReceiverDispatcher> rmap =
+ mReceivers.remove(context);
+ if (rmap != null) {
+ Iterator<ReceiverDispatcher> it = rmap.values().iterator();
+ while (it.hasNext()) {
+ ReceiverDispatcher rd = it.next();
+ IntentReceiverLeaked leak = new IntentReceiverLeaked(
+ what + " " + who + " has leaked IntentReceiver "
+ + rd.getIntentReceiver() + " that was " +
+ "originally registered here. Are you missing a " +
+ "call to unregisterReceiver()?");
+ leak.setStackTrace(rd.getLocation().getStackTrace());
+ Log.e(TAG, leak.getMessage(), leak);
+ try {
+ ActivityManagerNative.getDefault().unregisterReceiver(
+ rd.getIIntentReceiver());
+ } catch (RemoteException e) {
+ // system crashed, nothing we can do
+ }
+ }
+ }
+ mUnregisteredReceivers.remove(context);
+ //Log.i(TAG, "Receiver registrations: " + mReceivers);
+ HashMap<ServiceConnection, ServiceDispatcher> smap =
+ mServices.remove(context);
+ if (smap != null) {
+ Iterator<ServiceDispatcher> it = smap.values().iterator();
+ while (it.hasNext()) {
+ ServiceDispatcher sd = it.next();
+ ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
+ what + " " + who + " has leaked ServiceConnection "
+ + sd.getServiceConnection() + " that was originally bound here");
+ leak.setStackTrace(sd.getLocation().getStackTrace());
+ Log.e(TAG, leak.getMessage(), leak);
+ try {
+ ActivityManagerNative.getDefault().unbindService(
+ sd.getIServiceConnection());
+ } catch (RemoteException e) {
+ // system crashed, nothing we can do
+ }
+ sd.doForget();
+ }
+ }
+ mUnboundServices.remove(context);
+ //Log.i(TAG, "Service registrations: " + mServices);
+ }
+
+ public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
+ Context context, Handler handler,
+ Instrumentation instrumentation, boolean registered) {
+ synchronized (mReceivers) {
+ ReceiverDispatcher rd = null;
+ HashMap<BroadcastReceiver, ReceiverDispatcher> map = null;
+ if (registered) {
+ map = mReceivers.get(context);
+ if (map != null) {
+ rd = map.get(r);
+ }
+ }
+ if (rd == null) {
+ rd = new ReceiverDispatcher(r, context, handler,
+ instrumentation, registered);
+ if (registered) {
+ if (map == null) {
+ map = new HashMap<BroadcastReceiver, ReceiverDispatcher>();
+ mReceivers.put(context, map);
+ }
+ map.put(r, rd);
+ }
+ } else {
+ rd.validate(context, handler);
+ }
+ return rd.getIIntentReceiver();
+ }
+ }
+
+ public IIntentReceiver forgetReceiverDispatcher(Context context,
+ BroadcastReceiver r) {
+ synchronized (mReceivers) {
+ HashMap<BroadcastReceiver, ReceiverDispatcher> map = mReceivers.get(context);
+ ReceiverDispatcher rd = null;
+ if (map != null) {
+ rd = map.get(r);
+ if (rd != null) {
+ map.remove(r);
+ if (map.size() == 0) {
+ mReceivers.remove(context);
+ }
+ if (r.getDebugUnregister()) {
+ HashMap<BroadcastReceiver, ReceiverDispatcher> holder
+ = mUnregisteredReceivers.get(context);
+ if (holder == null) {
+ holder = new HashMap<BroadcastReceiver, ReceiverDispatcher>();
+ mUnregisteredReceivers.put(context, holder);
+ }
+ RuntimeException ex = new IllegalArgumentException(
+ "Originally unregistered here:");
+ ex.fillInStackTrace();
+ rd.setUnregisterLocation(ex);
+ holder.put(r, rd);
+ }
+ return rd.getIIntentReceiver();
+ }
+ }
+ HashMap<BroadcastReceiver, ReceiverDispatcher> holder
+ = mUnregisteredReceivers.get(context);
+ if (holder != null) {
+ rd = holder.get(r);
+ if (rd != null) {
+ RuntimeException ex = rd.getUnregisterLocation();
+ throw new IllegalArgumentException(
+ "Unregistering Receiver " + r
+ + " that was already unregistered", ex);
+ }
+ }
+ if (context == null) {
+ throw new IllegalStateException("Unbinding Receiver " + r
+ + " from Context that is no longer in use: " + context);
+ } else {
+ throw new IllegalArgumentException("Receiver not registered: " + r);
+ }
+
+ }
+ }
+
+ static final class ReceiverDispatcher {
+
+ final static class InnerReceiver extends IIntentReceiver.Stub {
+ final WeakReference<ReceiverDispatcher> mDispatcher;
+ final ReceiverDispatcher mStrongRef;
+
+ InnerReceiver(ReceiverDispatcher rd, boolean strong) {
+ mDispatcher = new WeakReference<ReceiverDispatcher>(rd);
+ mStrongRef = strong ? rd : null;
+ }
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered) {
+ ReceiverDispatcher rd = mDispatcher.get();
+ if (DEBUG_BROADCAST) {
+ int seq = intent.getIntExtra("seq", -1);
+ Log.i(TAG, "Receiving broadcast " + intent.getAction() + " seq=" + seq
+ + " to " + rd);
+ }
+ if (rd != null) {
+ rd.performReceive(intent, resultCode, data, extras, ordered);
+ }
+ }
+ }
+
+ final IIntentReceiver.Stub mIIntentReceiver;
+ final BroadcastReceiver mReceiver;
+ final Context mContext;
+ final Handler mActivityThread;
+ final Instrumentation mInstrumentation;
+ final boolean mRegistered;
+ final IntentReceiverLeaked mLocation;
+ RuntimeException mUnregisterLocation;
+
+ final class Args implements Runnable {
+ private Intent mCurIntent;
+ private int mCurCode;
+ private String mCurData;
+ private Bundle mCurMap;
+ private boolean mCurOrdered;
+
+ public void run() {
+ BroadcastReceiver receiver = mReceiver;
+ if (DEBUG_BROADCAST) {
+ int seq = mCurIntent.getIntExtra("seq", -1);
+ Log.i(TAG, "Dispathing broadcast " + mCurIntent.getAction() + " seq=" + seq
+ + " to " + mReceiver);
+ }
+ if (receiver == null) {
+ return;
+ }
+
+ IActivityManager mgr = ActivityManagerNative.getDefault();
+ Intent intent = mCurIntent;
+ mCurIntent = null;
+ try {
+ ClassLoader cl = mReceiver.getClass().getClassLoader();
+ intent.setExtrasClassLoader(cl);
+ if (mCurMap != null) {
+ mCurMap.setClassLoader(cl);
+ }
+ receiver.setOrderedHint(true);
+ receiver.setResult(mCurCode, mCurData, mCurMap);
+ receiver.clearAbortBroadcast();
+ receiver.setOrderedHint(mCurOrdered);
+ receiver.onReceive(mContext, intent);
+ } catch (Exception e) {
+ if (mRegistered && mCurOrdered) {
+ try {
+ mgr.finishReceiver(mIIntentReceiver,
+ mCurCode, mCurData, mCurMap, false);
+ } catch (RemoteException ex) {
+ }
+ }
+ if (mInstrumentation == null ||
+ !mInstrumentation.onException(mReceiver, e)) {
+ throw new RuntimeException(
+ "Error receiving broadcast " + intent
+ + " in " + mReceiver, e);
+ }
+ }
+ if (mRegistered && mCurOrdered) {
+ try {
+ mgr.finishReceiver(mIIntentReceiver,
+ receiver.getResultCode(),
+ receiver.getResultData(),
+ receiver.getResultExtras(false),
+ receiver.getAbortBroadcast());
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ ReceiverDispatcher(BroadcastReceiver receiver, Context context,
+ Handler activityThread, Instrumentation instrumentation,
+ boolean registered) {
+ if (activityThread == null) {
+ throw new NullPointerException("Handler must not be null");
+ }
+
+ mIIntentReceiver = new InnerReceiver(this, !registered);
+ mReceiver = receiver;
+ mContext = context;
+ mActivityThread = activityThread;
+ mInstrumentation = instrumentation;
+ mRegistered = registered;
+ mLocation = new IntentReceiverLeaked(null);
+ mLocation.fillInStackTrace();
+ }
+
+ void validate(Context context, Handler activityThread) {
+ if (mContext != context) {
+ throw new IllegalStateException(
+ "Receiver " + mReceiver +
+ " registered with differing Context (was " +
+ mContext + " now " + context + ")");
+ }
+ if (mActivityThread != activityThread) {
+ throw new IllegalStateException(
+ "Receiver " + mReceiver +
+ " registered with differing handler (was " +
+ mActivityThread + " now " + activityThread + ")");
+ }
+ }
+
+ IntentReceiverLeaked getLocation() {
+ return mLocation;
+ }
+
+ BroadcastReceiver getIntentReceiver() {
+ return mReceiver;
+ }
+
+ IIntentReceiver getIIntentReceiver() {
+ return mIIntentReceiver;
+ }
+
+ void setUnregisterLocation(RuntimeException ex) {
+ mUnregisterLocation = ex;
+ }
+
+ RuntimeException getUnregisterLocation() {
+ return mUnregisterLocation;
+ }
+
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered) {
+ if (DEBUG_BROADCAST) {
+ int seq = intent.getIntExtra("seq", -1);
+ Log.i(TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
+ + " to " + mReceiver);
+ }
+ Args args = new Args();
+ args.mCurIntent = intent;
+ args.mCurCode = resultCode;
+ args.mCurData = data;
+ args.mCurMap = extras;
+ args.mCurOrdered = ordered;
+ if (!mActivityThread.post(args)) {
+ if (mRegistered) {
+ IActivityManager mgr = ActivityManagerNative.getDefault();
+ try {
+ mgr.finishReceiver(mIIntentReceiver, args.mCurCode,
+ args.mCurData, args.mCurMap, false);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ }
+
+ public final IServiceConnection getServiceDispatcher(ServiceConnection c,
+ Context context, Handler handler, int flags) {
+ synchronized (mServices) {
+ ServiceDispatcher sd = null;
+ HashMap<ServiceConnection, ServiceDispatcher> map = mServices.get(context);
+ if (map != null) {
+ sd = map.get(c);
+ }
+ if (sd == null) {
+ sd = new ServiceDispatcher(c, context, handler, flags);
+ if (map == null) {
+ map = new HashMap<ServiceConnection, ServiceDispatcher>();
+ mServices.put(context, map);
+ }
+ map.put(c, sd);
+ } else {
+ sd.validate(context, handler);
+ }
+ return sd.getIServiceConnection();
+ }
+ }
+
+ public final IServiceConnection forgetServiceDispatcher(Context context,
+ ServiceConnection c) {
+ synchronized (mServices) {
+ HashMap<ServiceConnection, ServiceDispatcher> map
+ = mServices.get(context);
+ ServiceDispatcher sd = null;
+ if (map != null) {
+ sd = map.get(c);
+ if (sd != null) {
+ map.remove(c);
+ sd.doForget();
+ if (map.size() == 0) {
+ mServices.remove(context);
+ }
+ if ((sd.getFlags()&Context.BIND_DEBUG_UNBIND) != 0) {
+ HashMap<ServiceConnection, ServiceDispatcher> holder
+ = mUnboundServices.get(context);
+ if (holder == null) {
+ holder = new HashMap<ServiceConnection, ServiceDispatcher>();
+ mUnboundServices.put(context, holder);
+ }
+ RuntimeException ex = new IllegalArgumentException(
+ "Originally unbound here:");
+ ex.fillInStackTrace();
+ sd.setUnbindLocation(ex);
+ holder.put(c, sd);
+ }
+ return sd.getIServiceConnection();
+ }
+ }
+ HashMap<ServiceConnection, ServiceDispatcher> holder
+ = mUnboundServices.get(context);
+ if (holder != null) {
+ sd = holder.get(c);
+ if (sd != null) {
+ RuntimeException ex = sd.getUnbindLocation();
+ throw new IllegalArgumentException(
+ "Unbinding Service " + c
+ + " that was already unbound", ex);
+ }
+ }
+ if (context == null) {
+ throw new IllegalStateException("Unbinding Service " + c
+ + " from Context that is no longer in use: " + context);
+ } else {
+ throw new IllegalArgumentException("Service not registered: " + c);
+ }
+ }
+ }
+
+ static final class ServiceDispatcher {
+ private final InnerConnection mIServiceConnection;
+ private final ServiceConnection mConnection;
+ private final Context mContext;
+ private final Handler mActivityThread;
+ private final ServiceConnectionLeaked mLocation;
+ private final int mFlags;
+
+ private RuntimeException mUnbindLocation;
+
+ private boolean mDied;
+
+ private static class ConnectionInfo {
+ IBinder binder;
+ IBinder.DeathRecipient deathMonitor;
+ }
+
+ private static class InnerConnection extends IServiceConnection.Stub {
+ final WeakReference<ServiceDispatcher> mDispatcher;
+
+ InnerConnection(ServiceDispatcher sd) {
+ mDispatcher = new WeakReference<ServiceDispatcher>(sd);
+ }
+
+ public void connected(ComponentName name, IBinder service) throws RemoteException {
+ ServiceDispatcher sd = mDispatcher.get();
+ if (sd != null) {
+ sd.connected(name, service);
+ }
+ }
+ }
+
+ private final HashMap<ComponentName, ConnectionInfo> mActiveConnections
+ = new HashMap<ComponentName, ConnectionInfo>();
+
+ ServiceDispatcher(ServiceConnection conn,
+ Context context, Handler activityThread, int flags) {
+ mIServiceConnection = new InnerConnection(this);
+ mConnection = conn;
+ mContext = context;
+ mActivityThread = activityThread;
+ mLocation = new ServiceConnectionLeaked(null);
+ mLocation.fillInStackTrace();
+ mFlags = flags;
+ }
+
+ void validate(Context context, Handler activityThread) {
+ if (mContext != context) {
+ throw new RuntimeException(
+ "ServiceConnection " + mConnection +
+ " registered with differing Context (was " +
+ mContext + " now " + context + ")");
+ }
+ if (mActivityThread != activityThread) {
+ throw new RuntimeException(
+ "ServiceConnection " + mConnection +
+ " registered with differing handler (was " +
+ mActivityThread + " now " + activityThread + ")");
+ }
+ }
+
+ void doForget() {
+ synchronized(this) {
+ Iterator<ConnectionInfo> it = mActiveConnections.values().iterator();
+ while (it.hasNext()) {
+ ConnectionInfo ci = it.next();
+ ci.binder.unlinkToDeath(ci.deathMonitor, 0);
+ }
+ mActiveConnections.clear();
+ }
+ }
+
+ ServiceConnectionLeaked getLocation() {
+ return mLocation;
+ }
+
+ ServiceConnection getServiceConnection() {
+ return mConnection;
+ }
+
+ IServiceConnection getIServiceConnection() {
+ return mIServiceConnection;
+ }
+
+ int getFlags() {
+ return mFlags;
+ }
+
+ void setUnbindLocation(RuntimeException ex) {
+ mUnbindLocation = ex;
+ }
+
+ RuntimeException getUnbindLocation() {
+ return mUnbindLocation;
+ }
+
+ public void connected(ComponentName name, IBinder service) {
+ if (mActivityThread != null) {
+ mActivityThread.post(new RunConnection(name, service, 0));
+ } else {
+ doConnected(name, service);
+ }
+ }
+
+ public void death(ComponentName name, IBinder service) {
+ ConnectionInfo old;
+
+ synchronized (this) {
+ mDied = true;
+ old = mActiveConnections.remove(name);
+ if (old == null || old.binder != service) {
+ // Death for someone different than who we last
+ // reported... just ignore it.
+ return;
+ }
+ old.binder.unlinkToDeath(old.deathMonitor, 0);
+ }
+
+ if (mActivityThread != null) {
+ mActivityThread.post(new RunConnection(name, service, 1));
+ } else {
+ doDeath(name, service);
+ }
+ }
+
+ public void doConnected(ComponentName name, IBinder service) {
+ ConnectionInfo old;
+ ConnectionInfo info;
+
+ synchronized (this) {
+ old = mActiveConnections.get(name);
+ if (old != null && old.binder == service) {
+ // Huh, already have this one. Oh well!
+ return;
+ }
+
+ if (service != null) {
+ // A new service is being connected... set it all up.
+ mDied = false;
+ info = new ConnectionInfo();
+ info.binder = service;
+ info.deathMonitor = new DeathMonitor(name, service);
+ try {
+ service.linkToDeath(info.deathMonitor, 0);
+ mActiveConnections.put(name, info);
+ } catch (RemoteException e) {
+ // This service was dead before we got it... just
+ // don't do anything with it.
+ mActiveConnections.remove(name);
+ return;
+ }
+
+ } else {
+ // The named service is being disconnected... clean up.
+ mActiveConnections.remove(name);
+ }
+
+ if (old != null) {
+ old.binder.unlinkToDeath(old.deathMonitor, 0);
+ }
+ }
+
+ // If there was an old service, it is not disconnected.
+ if (old != null) {
+ mConnection.onServiceDisconnected(name);
+ }
+ // If there is a new service, it is now connected.
+ if (service != null) {
+ mConnection.onServiceConnected(name, service);
+ }
+ }
+
+ public void doDeath(ComponentName name, IBinder service) {
+ mConnection.onServiceDisconnected(name);
+ }
+
+ private final class RunConnection implements Runnable {
+ RunConnection(ComponentName name, IBinder service, int command) {
+ mName = name;
+ mService = service;
+ mCommand = command;
+ }
+
+ public void run() {
+ if (mCommand == 0) {
+ doConnected(mName, mService);
+ } else if (mCommand == 1) {
+ doDeath(mName, mService);
+ }
+ }
+
+ final ComponentName mName;
+ final IBinder mService;
+ final int mCommand;
+ }
+
+ private final class DeathMonitor implements IBinder.DeathRecipient
+ {
+ DeathMonitor(ComponentName name, IBinder service) {
+ mName = name;
+ mService = service;
+ }
+
+ public void binderDied() {
+ death(mName, mService);
+ }
+
+ final ComponentName mName;
+ final IBinder mService;
+ }
+ }
+ }
+
+ private static ApplicationContext mSystemContext = null;
+
+ private static final class ActivityRecord {
+ IBinder token;
+ Intent intent;
+ Bundle state;
+ Activity activity;
+ Window window;
+ Activity parent;
+ String embeddedID;
+ Object lastNonConfigurationInstance;
+ HashMap<String,Object> lastNonConfigurationChildInstances;
+ boolean paused;
+ boolean stopped;
+ boolean hideForNow;
+ Configuration newConfig;
+ ActivityRecord nextIdle;
+
+ ActivityInfo activityInfo;
+ PackageInfo packageInfo;
+
+ List<ResultInfo> pendingResults;
+ List<Intent> pendingIntents;
+
+ boolean startsNotResumed;
+ boolean isForward;
+
+ ActivityRecord() {
+ parent = null;
+ embeddedID = null;
+ paused = false;
+ stopped = false;
+ hideForNow = false;
+ nextIdle = null;
+ }
+
+ public String toString() {
+ ComponentName componentName = intent.getComponent();
+ return "ActivityRecord{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " token=" + token + " " + (componentName == null
+ ? "no component name" : componentName.toShortString())
+ + "}";
+ }
+ }
+
+ private final class ProviderRecord implements IBinder.DeathRecipient {
+ final String mName;
+ final IContentProvider mProvider;
+ final ContentProvider mLocalProvider;
+
+ ProviderRecord(String name, IContentProvider provider,
+ ContentProvider localProvider) {
+ mName = name;
+ mProvider = provider;
+ mLocalProvider = localProvider;
+ }
+
+ public void binderDied() {
+ removeDeadProvider(mName, mProvider);
+ }
+ }
+
+ private static final class NewIntentData {
+ List<Intent> intents;
+ IBinder token;
+ public String toString() {
+ return "NewIntentData{intents=" + intents + " token=" + token + "}";
+ }
+ }
+
+ private static final class ReceiverData {
+ Intent intent;
+ ActivityInfo info;
+ int resultCode;
+ String resultData;
+ Bundle resultExtras;
+ boolean sync;
+ boolean resultAbort;
+ public String toString() {
+ return "ReceiverData{intent=" + intent + " packageName=" +
+ info.packageName + " resultCode=" + resultCode
+ + " resultData=" + resultData + " resultExtras=" + resultExtras + "}";
+ }
+ }
+
+ private static final class CreateServiceData {
+ IBinder token;
+ ServiceInfo info;
+ Intent intent;
+ public String toString() {
+ return "CreateServiceData{token=" + token + " className="
+ + info.name + " packageName=" + info.packageName
+ + " intent=" + intent + "}";
+ }
+ }
+
+ private static final class BindServiceData {
+ IBinder token;
+ Intent intent;
+ boolean rebind;
+ public String toString() {
+ return "BindServiceData{token=" + token + " intent=" + intent + "}";
+ }
+ }
+
+ private static final class ServiceArgsData {
+ IBinder token;
+ int startId;
+ Intent args;
+ public String toString() {
+ return "ServiceArgsData{token=" + token + " startId=" + startId
+ + " args=" + args + "}";
+ }
+ }
+
+ private static final class AppBindData {
+ PackageInfo info;
+ String processName;
+ ApplicationInfo appInfo;
+ List<ProviderInfo> providers;
+ ComponentName instrumentationName;
+ String profileFile;
+ Bundle instrumentationArgs;
+ IInstrumentationWatcher instrumentationWatcher;
+ int debugMode;
+ Configuration config;
+ boolean handlingProfiling;
+ public String toString() {
+ return "AppBindData{appInfo=" + appInfo + "}";
+ }
+ }
+
+ private static final class DumpServiceInfo {
+ FileDescriptor fd;
+ IBinder service;
+ String[] args;
+ boolean dumped;
+ }
+
+ private static final class ResultData {
+ IBinder token;
+ List<ResultInfo> results;
+ public String toString() {
+ return "ResultData{token=" + token + " results" + results + "}";
+ }
+ }
+
+ private static final class ContextCleanupInfo {
+ ApplicationContext context;
+ String what;
+ String who;
+ }
+
+ private final class ApplicationThread extends ApplicationThreadNative {
+ private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
+ private static final String ONE_COUNT_COLUMN = "%17s %8d";
+ private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d";
+
+ // Formatting for checkin service - update version if row format changes
+ private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1;
+
+ public final void schedulePauseActivity(IBinder token, boolean finished,
+ boolean userLeaving, int configChanges) {
+ queueOrSendMessage(
+ finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
+ token,
+ (userLeaving ? 1 : 0),
+ configChanges);
+ }
+
+ public final void scheduleStopActivity(IBinder token, boolean showWindow,
+ int configChanges) {
+ queueOrSendMessage(
+ showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
+ token, 0, configChanges);
+ }
+
+ public final void scheduleWindowVisibility(IBinder token, boolean showWindow) {
+ queueOrSendMessage(
+ showWindow ? H.SHOW_WINDOW : H.HIDE_WINDOW,
+ token);
+ }
+
+ public final void scheduleResumeActivity(IBinder token, boolean isForward) {
+ queueOrSendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
+ }
+
+ public final void scheduleSendResult(IBinder token, List<ResultInfo> results) {
+ ResultData res = new ResultData();
+ res.token = token;
+ res.results = results;
+ queueOrSendMessage(H.SEND_RESULT, res);
+ }
+
+ // we use token to identify this activity without having to send the
+ // activity itself back to the activity manager. (matters more with ipc)
+ public final void scheduleLaunchActivity(Intent intent, IBinder token,
+ ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
+ List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) {
+ ActivityRecord r = new ActivityRecord();
+
+ r.token = token;
+ r.intent = intent;
+ r.activityInfo = info;
+ r.state = state;
+
+ r.pendingResults = pendingResults;
+ r.pendingIntents = pendingNewIntents;
+
+ r.startsNotResumed = notResumed;
+ r.isForward = isForward;
+
+ queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
+ }
+
+ public final void scheduleRelaunchActivity(IBinder token,
+ List<ResultInfo> pendingResults, List<Intent> pendingNewIntents,
+ int configChanges, boolean notResumed) {
+ ActivityRecord r = new ActivityRecord();
+
+ r.token = token;
+ r.pendingResults = pendingResults;
+ r.pendingIntents = pendingNewIntents;
+ r.startsNotResumed = notResumed;
+
+ synchronized (mRelaunchingActivities) {
+ mRelaunchingActivities.add(r);
+ }
+
+ queueOrSendMessage(H.RELAUNCH_ACTIVITY, r, configChanges);
+ }
+
+ public final void scheduleNewIntent(List<Intent> intents, IBinder token) {
+ NewIntentData data = new NewIntentData();
+ data.intents = intents;
+ data.token = token;
+
+ queueOrSendMessage(H.NEW_INTENT, data);
+ }
+
+ public final void scheduleDestroyActivity(IBinder token, boolean finishing,
+ int configChanges) {
+ queueOrSendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,
+ configChanges);
+ }
+
+ public final void scheduleReceiver(Intent intent, ActivityInfo info,
+ int resultCode, String data, Bundle extras, boolean sync) {
+ ReceiverData r = new ReceiverData();
+
+ r.intent = intent;
+ r.info = info;
+ r.resultCode = resultCode;
+ r.resultData = data;
+ r.resultExtras = extras;
+ r.sync = sync;
+
+ queueOrSendMessage(H.RECEIVER, r);
+ }
+
+ public final void scheduleCreateService(IBinder token,
+ ServiceInfo info) {
+ CreateServiceData s = new CreateServiceData();
+ s.token = token;
+ s.info = info;
+
+ queueOrSendMessage(H.CREATE_SERVICE, s);
+ }
+
+ public final void scheduleBindService(IBinder token, Intent intent,
+ boolean rebind) {
+ BindServiceData s = new BindServiceData();
+ s.token = token;
+ s.intent = intent;
+ s.rebind = rebind;
+
+ queueOrSendMessage(H.BIND_SERVICE, s);
+ }
+
+ public final void scheduleUnbindService(IBinder token, Intent intent) {
+ BindServiceData s = new BindServiceData();
+ s.token = token;
+ s.intent = intent;
+
+ queueOrSendMessage(H.UNBIND_SERVICE, s);
+ }
+
+ public final void scheduleServiceArgs(IBinder token, int startId,
+ Intent args) {
+ ServiceArgsData s = new ServiceArgsData();
+ s.token = token;
+ s.startId = startId;
+ s.args = args;
+
+ queueOrSendMessage(H.SERVICE_ARGS, s);
+ }
+
+ public final void scheduleStopService(IBinder token) {
+ queueOrSendMessage(H.STOP_SERVICE, token);
+ }
+
+ public final void bindApplication(String processName,
+ ApplicationInfo appInfo, List<ProviderInfo> providers,
+ ComponentName instrumentationName, String profileFile,
+ Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
+ int debugMode, Configuration config,
+ Map<String, IBinder> services) {
+ Process.setArgV0(processName);
+
+ if (services != null) {
+ // Setup the service cache in the ServiceManager
+ ServiceManager.initServiceCache(services);
+ }
+
+ AppBindData data = new AppBindData();
+ data.processName = processName;
+ data.appInfo = appInfo;
+ data.providers = providers;
+ data.instrumentationName = instrumentationName;
+ data.profileFile = profileFile;
+ data.instrumentationArgs = instrumentationArgs;
+ data.instrumentationWatcher = instrumentationWatcher;
+ data.debugMode = debugMode;
+ data.config = config;
+ queueOrSendMessage(H.BIND_APPLICATION, data);
+ }
+
+ public final void scheduleExit() {
+ queueOrSendMessage(H.EXIT_APPLICATION, null);
+ }
+
+ public void requestThumbnail(IBinder token) {
+ queueOrSendMessage(H.REQUEST_THUMBNAIL, token);
+ }
+
+ public void scheduleConfigurationChanged(Configuration config) {
+ synchronized (mRelaunchingActivities) {
+ mPendingConfiguration = config;
+ }
+ queueOrSendMessage(H.CONFIGURATION_CHANGED, config);
+ }
+
+ public void updateTimeZone() {
+ TimeZone.setDefault(null);
+ }
+
+ public void processInBackground() {
+ mH.removeMessages(H.GC_WHEN_IDLE);
+ mH.sendMessage(mH.obtainMessage(H.GC_WHEN_IDLE));
+ }
+
+ public void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args) {
+ DumpServiceInfo data = new DumpServiceInfo();
+ data.fd = fd;
+ data.service = servicetoken;
+ data.args = args;
+ data.dumped = false;
+ queueOrSendMessage(H.DUMP_SERVICE, data);
+ synchronized (data) {
+ while (!data.dumped) {
+ try {
+ data.wait();
+ } catch (InterruptedException e) {
+ // no need to do anything here, we will keep waiting until
+ // dumped is set
+ }
+ }
+ }
+ }
+
+ // This function exists to make sure all receiver dispatching is
+ // correctly ordered, since these are one-way calls and the binder driver
+ // applies transaction ordering per object for such calls.
+ public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
+ int resultCode, String dataStr, Bundle extras, boolean ordered)
+ throws RemoteException {
+ receiver.performReceive(intent, resultCode, dataStr, extras, ordered);
+ }
+
+ public void scheduleLowMemory() {
+ queueOrSendMessage(H.LOW_MEMORY, null);
+ }
+
+ public void scheduleActivityConfigurationChanged(IBinder token) {
+ queueOrSendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token);
+ }
+
+ public void requestPss() {
+ try {
+ ActivityManagerNative.getDefault().reportPss(this,
+ (int)Process.getPss(Process.myPid()));
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ long nativeMax = Debug.getNativeHeapSize() / 1024;
+ long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
+ long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
+
+ Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
+ Debug.getMemoryInfo(memInfo);
+
+ final int nativeShared = memInfo.nativeSharedDirty;
+ final int dalvikShared = memInfo.dalvikSharedDirty;
+ final int otherShared = memInfo.otherSharedDirty;
+
+ final int nativePrivate = memInfo.nativePrivateDirty;
+ final int dalvikPrivate = memInfo.dalvikPrivateDirty;
+ final int otherPrivate = memInfo.otherPrivateDirty;
+
+ Runtime runtime = Runtime.getRuntime();
+
+ long dalvikMax = runtime.totalMemory() / 1024;
+ long dalvikFree = runtime.freeMemory() / 1024;
+ long dalvikAllocated = dalvikMax - dalvikFree;
+ long viewInstanceCount = ViewDebug.getViewInstanceCount();
+ long viewRootInstanceCount = ViewDebug.getViewRootInstanceCount();
+ long appContextInstanceCount = ApplicationContext.getInstanceCount();
+ long activityInstanceCount = Activity.getInstanceCount();
+ int globalAssetCount = AssetManager.getGlobalAssetCount();
+ int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount();
+ int binderLocalObjectCount = Debug.getBinderLocalObjectCount();
+ int binderProxyObjectCount = Debug.getBinderProxyObjectCount();
+ int binderDeathObjectCount = Debug.getBinderDeathObjectCount();
+ int openSslSocketCount = OpenSSLSocketImpl.getInstanceCount();
+ long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024;
+ SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats();
+ SQLiteDebug.getPagerStats(stats);
+
+ // Check to see if we were called by checkin server. If so, print terse format.
+ boolean doCheckinFormat = false;
+ if (args != null) {
+ for (String arg : args) {
+ if ("-c".equals(arg)) doCheckinFormat = true;
+ }
+ }
+
+ // For checkin, we print one long comma-separated list of values
+ if (doCheckinFormat) {
+ // NOTE: if you change anything significant below, also consider changing
+ // ACTIVITY_THREAD_CHECKIN_VERSION.
+ String processName = (mBoundApplication != null)
+ ? mBoundApplication.processName : "unknown";
+
+ // Header
+ pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(',');
+ pw.print(Process.myPid()); pw.print(',');
+ pw.print(processName); pw.print(',');
+
+ // Heap info - max
+ pw.print(nativeMax); pw.print(',');
+ pw.print(dalvikMax); pw.print(',');
+ pw.print("N/A,");
+ pw.print(nativeMax + dalvikMax); pw.print(',');
+
+ // Heap info - allocated
+ pw.print(nativeAllocated); pw.print(',');
+ pw.print(dalvikAllocated); pw.print(',');
+ pw.print("N/A,");
+ pw.print(nativeAllocated + dalvikAllocated); pw.print(',');
+
+ // Heap info - free
+ pw.print(nativeFree); pw.print(',');
+ pw.print(dalvikFree); pw.print(',');
+ pw.print("N/A,");
+ pw.print(nativeFree + dalvikFree); pw.print(',');
+
+ // Heap info - proportional set size
+ pw.print(memInfo.nativePss); pw.print(',');
+ pw.print(memInfo.dalvikPss); pw.print(',');
+ pw.print(memInfo.otherPss); pw.print(',');
+ pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(',');
+
+ // Heap info - shared
+ pw.print(nativeShared); pw.print(',');
+ pw.print(dalvikShared); pw.print(',');
+ pw.print(otherShared); pw.print(',');
+ pw.print(nativeShared + dalvikShared + otherShared); pw.print(',');
+
+ // Heap info - private
+ pw.print(nativePrivate); pw.print(',');
+ pw.print(dalvikPrivate); pw.print(',');
+ pw.print(otherPrivate); pw.print(',');
+ pw.print(nativePrivate + dalvikPrivate + otherPrivate); pw.print(',');
+
+ // Object counts
+ pw.print(viewInstanceCount); pw.print(',');
+ pw.print(viewRootInstanceCount); pw.print(',');
+ pw.print(appContextInstanceCount); pw.print(',');
+ pw.print(activityInstanceCount); pw.print(',');
+
+ pw.print(globalAssetCount); pw.print(',');
+ pw.print(globalAssetManagerCount); pw.print(',');
+ pw.print(binderLocalObjectCount); pw.print(',');
+ pw.print(binderProxyObjectCount); pw.print(',');
+
+ pw.print(binderDeathObjectCount); pw.print(',');
+ pw.print(openSslSocketCount); pw.print(',');
+
+ // SQL
+ pw.print(sqliteAllocated); pw.print(',');
+ pw.print(stats.databaseBytes / 1024); pw.print(',');
+ pw.print(stats.numPagers); pw.print(',');
+ pw.print((stats.totalBytes - stats.referencedBytes) / 1024); pw.print(',');
+ pw.print(stats.referencedBytes / 1024); pw.print('\n');
+
+ return;
+ }
+
+ // otherwise, show human-readable format
+ printRow(pw, HEAP_COLUMN, "", "native", "dalvik", "other", "total");
+ printRow(pw, HEAP_COLUMN, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax);
+ printRow(pw, HEAP_COLUMN, "allocated:", nativeAllocated, dalvikAllocated, "N/A",
+ nativeAllocated + dalvikAllocated);
+ printRow(pw, HEAP_COLUMN, "free:", nativeFree, dalvikFree, "N/A",
+ nativeFree + dalvikFree);
+
+ printRow(pw, HEAP_COLUMN, "(Pss):", memInfo.nativePss, memInfo.dalvikPss,
+ memInfo.otherPss, memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss);
+
+ printRow(pw, HEAP_COLUMN, "(shared dirty):", nativeShared, dalvikShared, otherShared,
+ nativeShared + dalvikShared + otherShared);
+ printRow(pw, HEAP_COLUMN, "(priv dirty):", nativePrivate, dalvikPrivate, otherPrivate,
+ nativePrivate + dalvikPrivate + otherPrivate);
+
+ pw.println(" ");
+ pw.println(" Objects");
+ printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRoots:",
+ viewRootInstanceCount);
+
+ printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", appContextInstanceCount,
+ "Activities:", activityInstanceCount);
+
+ printRow(pw, TWO_COUNT_COLUMNS, "Assets:", globalAssetCount,
+ "AssetManagers:", globalAssetManagerCount);
+
+ printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", binderLocalObjectCount,
+ "Proxy Binders:", binderProxyObjectCount);
+ printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", binderDeathObjectCount);
+
+ printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", openSslSocketCount);
+
+ // SQLite mem info
+ pw.println(" ");
+ pw.println(" SQL");
+ printRow(pw, TWO_COUNT_COLUMNS, "heap:", sqliteAllocated, "dbFiles:",
+ stats.databaseBytes / 1024);
+ printRow(pw, TWO_COUNT_COLUMNS, "numPagers:", stats.numPagers, "inactivePageKB:",
+ (stats.totalBytes - stats.referencedBytes) / 1024);
+ printRow(pw, ONE_COUNT_COLUMN, "activePageKB:", stats.referencedBytes / 1024);
+ }
+
+ private void printRow(PrintWriter pw, String format, Object...objs) {
+ pw.println(String.format(format, objs));
+ }
+ }
+
+ private final class H extends Handler {
+ public static final int LAUNCH_ACTIVITY = 100;
+ public static final int PAUSE_ACTIVITY = 101;
+ public static final int PAUSE_ACTIVITY_FINISHING= 102;
+ public static final int STOP_ACTIVITY_SHOW = 103;
+ public static final int STOP_ACTIVITY_HIDE = 104;
+ public static final int SHOW_WINDOW = 105;
+ public static final int HIDE_WINDOW = 106;
+ public static final int RESUME_ACTIVITY = 107;
+ public static final int SEND_RESULT = 108;
+ public static final int DESTROY_ACTIVITY = 109;
+ public static final int BIND_APPLICATION = 110;
+ public static final int EXIT_APPLICATION = 111;
+ public static final int NEW_INTENT = 112;
+ public static final int RECEIVER = 113;
+ public static final int CREATE_SERVICE = 114;
+ public static final int SERVICE_ARGS = 115;
+ public static final int STOP_SERVICE = 116;
+ public static final int REQUEST_THUMBNAIL = 117;
+ public static final int CONFIGURATION_CHANGED = 118;
+ public static final int CLEAN_UP_CONTEXT = 119;
+ public static final int GC_WHEN_IDLE = 120;
+ public static final int BIND_SERVICE = 121;
+ public static final int UNBIND_SERVICE = 122;
+ public static final int DUMP_SERVICE = 123;
+ public static final int LOW_MEMORY = 124;
+ public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;
+ public static final int RELAUNCH_ACTIVITY = 126;
+ String codeToString(int code) {
+ if (localLOGV) {
+ switch (code) {
+ case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
+ case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";
+ case PAUSE_ACTIVITY_FINISHING: return "PAUSE_ACTIVITY_FINISHING";
+ case STOP_ACTIVITY_SHOW: return "STOP_ACTIVITY_SHOW";
+ case STOP_ACTIVITY_HIDE: return "STOP_ACTIVITY_HIDE";
+ case SHOW_WINDOW: return "SHOW_WINDOW";
+ case HIDE_WINDOW: return "HIDE_WINDOW";
+ case RESUME_ACTIVITY: return "RESUME_ACTIVITY";
+ case SEND_RESULT: return "SEND_RESULT";
+ case DESTROY_ACTIVITY: return "DESTROY_ACTIVITY";
+ case BIND_APPLICATION: return "BIND_APPLICATION";
+ case EXIT_APPLICATION: return "EXIT_APPLICATION";
+ case NEW_INTENT: return "NEW_INTENT";
+ case RECEIVER: return "RECEIVER";
+ case CREATE_SERVICE: return "CREATE_SERVICE";
+ case SERVICE_ARGS: return "SERVICE_ARGS";
+ case STOP_SERVICE: return "STOP_SERVICE";
+ case REQUEST_THUMBNAIL: return "REQUEST_THUMBNAIL";
+ case CONFIGURATION_CHANGED: return "CONFIGURATION_CHANGED";
+ case CLEAN_UP_CONTEXT: return "CLEAN_UP_CONTEXT";
+ case GC_WHEN_IDLE: return "GC_WHEN_IDLE";
+ case BIND_SERVICE: return "BIND_SERVICE";
+ case UNBIND_SERVICE: return "UNBIND_SERVICE";
+ case DUMP_SERVICE: return "DUMP_SERVICE";
+ case LOW_MEMORY: return "LOW_MEMORY";
+ case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED";
+ case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
+ }
+ }
+ return "(unknown)";
+ }
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case LAUNCH_ACTIVITY: {
+ ActivityRecord r = (ActivityRecord)msg.obj;
+
+ r.packageInfo = getPackageInfoNoCheck(
+ r.activityInfo.applicationInfo);
+ handleLaunchActivity(r);
+ } break;
+ case RELAUNCH_ACTIVITY: {
+ ActivityRecord r = (ActivityRecord)msg.obj;
+ handleRelaunchActivity(r, msg.arg1);
+ } break;
+ case PAUSE_ACTIVITY:
+ handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
+ break;
+ case PAUSE_ACTIVITY_FINISHING:
+ handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
+ break;
+ case STOP_ACTIVITY_SHOW:
+ handleStopActivity((IBinder)msg.obj, true, msg.arg2);
+ break;
+ case STOP_ACTIVITY_HIDE:
+ handleStopActivity((IBinder)msg.obj, false, msg.arg2);
+ break;
+ case SHOW_WINDOW:
+ handleWindowVisibility((IBinder)msg.obj, true);
+ break;
+ case HIDE_WINDOW:
+ handleWindowVisibility((IBinder)msg.obj, false);
+ break;
+ case RESUME_ACTIVITY:
+ handleResumeActivity((IBinder)msg.obj, true,
+ msg.arg1 != 0);
+ break;
+ case SEND_RESULT:
+ handleSendResult((ResultData)msg.obj);
+ break;
+ case DESTROY_ACTIVITY:
+ handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0,
+ msg.arg2, false);
+ break;
+ case BIND_APPLICATION:
+ AppBindData data = (AppBindData)msg.obj;
+ handleBindApplication(data);
+ break;
+ case EXIT_APPLICATION:
+ if (mInitialApplication != null) {
+ mInitialApplication.onTerminate();
+ }
+ Looper.myLooper().quit();
+ break;
+ case NEW_INTENT:
+ handleNewIntent((NewIntentData)msg.obj);
+ break;
+ case RECEIVER:
+ handleReceiver((ReceiverData)msg.obj);
+ break;
+ case CREATE_SERVICE:
+ handleCreateService((CreateServiceData)msg.obj);
+ break;
+ case BIND_SERVICE:
+ handleBindService((BindServiceData)msg.obj);
+ break;
+ case UNBIND_SERVICE:
+ handleUnbindService((BindServiceData)msg.obj);
+ break;
+ case SERVICE_ARGS:
+ handleServiceArgs((ServiceArgsData)msg.obj);
+ break;
+ case STOP_SERVICE:
+ handleStopService((IBinder)msg.obj);
+ break;
+ case REQUEST_THUMBNAIL:
+ handleRequestThumbnail((IBinder)msg.obj);
+ break;
+ case CONFIGURATION_CHANGED:
+ handleConfigurationChanged((Configuration)msg.obj);
+ break;
+ case CLEAN_UP_CONTEXT:
+ ContextCleanupInfo cci = (ContextCleanupInfo)msg.obj;
+ cci.context.performFinalCleanup(cci.who, cci.what);
+ break;
+ case GC_WHEN_IDLE:
+ scheduleGcIdler();
+ break;
+ case DUMP_SERVICE:
+ handleDumpService((DumpServiceInfo)msg.obj);
+ break;
+ case LOW_MEMORY:
+ handleLowMemory();
+ break;
+ case ACTIVITY_CONFIGURATION_CHANGED:
+ handleActivityConfigurationChanged((IBinder)msg.obj);
+ break;
+ }
+ }
+ }
+
+ private final class Idler implements MessageQueue.IdleHandler {
+ public final boolean queueIdle() {
+ ActivityRecord a = mNewActivities;
+ if (a != null) {
+ mNewActivities = null;
+ IActivityManager am = ActivityManagerNative.getDefault();
+ ActivityRecord prev;
+ do {
+ if (localLOGV) Log.v(
+ TAG, "Reporting idle of " + a +
+ " finished=" +
+ (a.activity != null ? a.activity.mFinished : false));
+ if (a.activity != null && !a.activity.mFinished) {
+ try {
+ am.activityIdle(a.token);
+ } catch (RemoteException ex) {
+ }
+ }
+ prev = a;
+ a = a.nextIdle;
+ prev.nextIdle = null;
+ } while (a != null);
+ }
+ return false;
+ }
+ }
+
+ final class GcIdler implements MessageQueue.IdleHandler {
+ public final boolean queueIdle() {
+ doGcIfNeeded();
+ return false;
+ }
+ }
+
+ static IPackageManager sPackageManager;
+
+ final ApplicationThread mAppThread = new ApplicationThread();
+ final Looper mLooper = Looper.myLooper();
+ final H mH = new H();
+ final HashMap<IBinder, ActivityRecord> mActivities
+ = new HashMap<IBinder, ActivityRecord>();
+ // List of new activities (via ActivityRecord.nextIdle) that should
+ // be reported when next we idle.
+ ActivityRecord mNewActivities = null;
+ // Number of activities that are currently visible on-screen.
+ int mNumVisibleActivities = 0;
+ final HashMap<IBinder, Service> mServices
+ = new HashMap<IBinder, Service>();
+ AppBindData mBoundApplication;
+ Configuration mConfiguration;
+ Application mInitialApplication;
+ final ArrayList<Application> mAllApplications
+ = new ArrayList<Application>();
+ static final ThreadLocal sThreadLocal = new ThreadLocal();
+ Instrumentation mInstrumentation;
+ String mInstrumentationAppDir = null;
+ String mInstrumentationAppPackage = null;
+ String mInstrumentedAppDir = null;
+ boolean mSystemThread = false;
+
+ /**
+ * Activities that are enqueued to be relaunched. This list is accessed
+ * by multiple threads, so you must synchronize on it when accessing it.
+ */
+ final ArrayList<ActivityRecord> mRelaunchingActivities
+ = new ArrayList<ActivityRecord>();
+ Configuration mPendingConfiguration = null;
+
+ // These can be accessed by multiple threads; mPackages is the lock.
+ // XXX For now we keep around information about all packages we have
+ // seen, not removing entries from this map.
+ final HashMap<String, WeakReference<PackageInfo>> mPackages
+ = new HashMap<String, WeakReference<PackageInfo>>();
+ final HashMap<String, WeakReference<PackageInfo>> mResourcePackages
+ = new HashMap<String, WeakReference<PackageInfo>>();
+ Display mDisplay = null;
+ DisplayMetrics mDisplayMetrics = null;
+ HashMap<String, WeakReference<Resources> > mActiveResources
+ = new HashMap<String, WeakReference<Resources> >();
+
+ // The lock of mProviderMap protects the following variables.
+ final HashMap<String, ProviderRecord> mProviderMap
+ = new HashMap<String, ProviderRecord>();
+ final HashMap<IBinder, ProviderRefCount> mProviderRefCountMap
+ = new HashMap<IBinder, ProviderRefCount>();
+ final HashMap<IBinder, ProviderRecord> mLocalProviders
+ = new HashMap<IBinder, ProviderRecord>();
+
+ final GcIdler mGcIdler = new GcIdler();
+ boolean mGcIdlerScheduled = false;
+
+ public final PackageInfo getPackageInfo(String packageName, int flags) {
+ synchronized (mPackages) {
+ WeakReference<PackageInfo> ref;
+ if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) {
+ ref = mPackages.get(packageName);
+ } else {
+ ref = mResourcePackages.get(packageName);
+ }
+ PackageInfo packageInfo = ref != null ? ref.get() : null;
+ //Log.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
+ if (packageInfo != null && (packageInfo.mResources == null
+ || packageInfo.mResources.getAssets().isUpToDate())) {
+ if (packageInfo.isSecurityViolation()
+ && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
+ throw new SecurityException(
+ "Requesting code from " + packageName
+ + " to be run in process "
+ + mBoundApplication.processName
+ + "/" + mBoundApplication.appInfo.uid);
+ }
+ return packageInfo;
+ }
+ }
+
+ ApplicationInfo ai = null;
+ try {
+ ai = getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_SHARED_LIBRARY_FILES);
+ } catch (RemoteException e) {
+ }
+
+ if (ai != null) {
+ return getPackageInfo(ai, flags);
+ }
+
+ return null;
+ }
+
+ public final PackageInfo getPackageInfo(ApplicationInfo ai, int flags) {
+ boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
+ boolean securityViolation = includeCode && ai.uid != 0
+ && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
+ ? ai.uid != mBoundApplication.appInfo.uid : true);
+ if ((flags&(Context.CONTEXT_INCLUDE_CODE
+ |Context.CONTEXT_IGNORE_SECURITY))
+ == Context.CONTEXT_INCLUDE_CODE) {
+ if (securityViolation) {
+ String msg = "Requesting code from " + ai.packageName
+ + " (with uid " + ai.uid + ")";
+ if (mBoundApplication != null) {
+ msg = msg + " to be run in process "
+ + mBoundApplication.processName + " (with uid "
+ + mBoundApplication.appInfo.uid + ")";
+ }
+ throw new SecurityException(msg);
+ }
+ }
+ return getPackageInfo(ai, null, securityViolation, includeCode);
+ }
+
+ public final PackageInfo getPackageInfoNoCheck(ApplicationInfo ai) {
+ return getPackageInfo(ai, null, false, true);
+ }
+
+ private final PackageInfo getPackageInfo(ApplicationInfo aInfo,
+ ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
+ synchronized (mPackages) {
+ WeakReference<PackageInfo> ref;
+ if (includeCode) {
+ ref = mPackages.get(aInfo.packageName);
+ } else {
+ ref = mResourcePackages.get(aInfo.packageName);
+ }
+ PackageInfo packageInfo = ref != null ? ref.get() : null;
+ if (packageInfo == null || (packageInfo.mResources != null
+ && !packageInfo.mResources.getAssets().isUpToDate())) {
+ if (localLOGV) Log.v(TAG, (includeCode ? "Loading code package "
+ : "Loading resource-only package ") + aInfo.packageName
+ + " (in " + (mBoundApplication != null
+ ? mBoundApplication.processName : null)
+ + ")");
+ packageInfo =
+ new PackageInfo(this, aInfo, this, baseLoader,
+ securityViolation, includeCode &&
+ (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
+ if (includeCode) {
+ mPackages.put(aInfo.packageName,
+ new WeakReference<PackageInfo>(packageInfo));
+ } else {
+ mResourcePackages.put(aInfo.packageName,
+ new WeakReference<PackageInfo>(packageInfo));
+ }
+ }
+ return packageInfo;
+ }
+ }
+
+ public final boolean hasPackageInfo(String packageName) {
+ synchronized (mPackages) {
+ WeakReference<PackageInfo> ref;
+ ref = mPackages.get(packageName);
+ if (ref != null && ref.get() != null) {
+ return true;
+ }
+ ref = mResourcePackages.get(packageName);
+ if (ref != null && ref.get() != null) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ ActivityThread() {
+ }
+
+ public ApplicationThread getApplicationThread()
+ {
+ return mAppThread;
+ }
+
+ public Instrumentation getInstrumentation()
+ {
+ return mInstrumentation;
+ }
+
+ public Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ public boolean isProfiling() {
+ return mBoundApplication != null && mBoundApplication.profileFile != null;
+ }
+
+ public String getProfileFilePath() {
+ return mBoundApplication.profileFile;
+ }
+
+ public Looper getLooper() {
+ return mLooper;
+ }
+
+ public Application getApplication() {
+ return mInitialApplication;
+ }
+
+ public ApplicationContext getSystemContext() {
+ synchronized (this) {
+ if (mSystemContext == null) {
+ ApplicationContext context =
+ ApplicationContext.createSystemContext(this);
+ PackageInfo info = new PackageInfo(this, "android", context);
+ context.init(info, null, this);
+ context.getResources().updateConfiguration(
+ getConfiguration(), getDisplayMetricsLocked(false));
+ mSystemContext = context;
+ //Log.i(TAG, "Created system resources " + context.getResources()
+ // + ": " + context.getResources().getConfiguration());
+ }
+ }
+ return mSystemContext;
+ }
+
+ void scheduleGcIdler() {
+ if (!mGcIdlerScheduled) {
+ mGcIdlerScheduled = true;
+ Looper.myQueue().addIdleHandler(mGcIdler);
+ }
+ mH.removeMessages(H.GC_WHEN_IDLE);
+ }
+
+ void unscheduleGcIdler() {
+ if (mGcIdlerScheduled) {
+ mGcIdlerScheduled = false;
+ Looper.myQueue().removeIdleHandler(mGcIdler);
+ }
+ mH.removeMessages(H.GC_WHEN_IDLE);
+ }
+
+ void doGcIfNeeded() {
+ mGcIdlerScheduled = false;
+ final long now = SystemClock.uptimeMillis();
+ //Log.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime()
+ // + "m now=" + now);
+ if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
+ //Log.i(TAG, "**** WE DO, WE DO WANT TO GC!");
+ BinderInternal.forceGc("bg");
+ }
+ }
+
+ public final ActivityInfo resolveActivityInfo(Intent intent) {
+ ActivityInfo aInfo = intent.resolveActivityInfo(
+ mInitialApplication.getPackageManager(), PackageManager.GET_SHARED_LIBRARY_FILES);
+ if (aInfo == null) {
+ // Throw an exception.
+ Instrumentation.checkStartActivityResult(
+ IActivityManager.START_CLASS_NOT_FOUND, intent);
+ }
+ return aInfo;
+ }
+
+ public final Activity startActivityNow(Activity parent, String id,
+ Intent intent, IBinder token, Bundle state) {
+ ActivityInfo aInfo = resolveActivityInfo(intent);
+ return startActivityNow(parent, id, intent, aInfo, token, state);
+ }
+
+ public final Activity startActivityNow(Activity parent, String id,
+ Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state) {
+ return startActivityNow(parent, id, intent, activityInfo, token, state, null);
+ }
+
+ public final Activity startActivityNow(Activity parent, String id,
+ Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
+ Object lastNonConfigurationInstance) {
+ ActivityRecord r = new ActivityRecord();
+ r.token = token;
+ r.intent = intent;
+ r.state = state;
+ r.parent = parent;
+ r.embeddedID = id;
+ r.activityInfo = activityInfo;
+ r.lastNonConfigurationInstance = lastNonConfigurationInstance;
+ if (localLOGV) {
+ ComponentName compname = intent.getComponent();
+ String name;
+ if (compname != null) {
+ name = compname.toShortString();
+ } else {
+ name = "(Intent " + intent + ").getComponent() returned null";
+ }
+ Log.v(TAG, "Performing launch: action=" + intent.getAction()
+ + ", comp=" + name
+ + ", token=" + token);
+ }
+ return performLaunchActivity(r);
+ }
+
+ public final Activity getActivity(IBinder token) {
+ return mActivities.get(token).activity;
+ }
+
+ public final void sendActivityResult(
+ IBinder token, String id, int requestCode,
+ int resultCode, Intent data) {
+ ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+ list.add(new ResultInfo(id, requestCode, resultCode, data));
+ mAppThread.scheduleSendResult(token, list);
+ }
+
+ // if the thread hasn't started yet, we don't have the handler, so just
+ // save the messages until we're ready.
+ private final void queueOrSendMessage(int what, Object obj) {
+ queueOrSendMessage(what, obj, 0, 0);
+ }
+
+ private final void queueOrSendMessage(int what, Object obj, int arg1) {
+ queueOrSendMessage(what, obj, arg1, 0);
+ }
+
+ private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
+ synchronized (this) {
+ if (localLOGV) Log.v(
+ TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
+ + ": " + arg1 + " / " + obj);
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.obj = obj;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ mH.sendMessage(msg);
+ }
+ }
+
+ final void scheduleContextCleanup(ApplicationContext context, String who,
+ String what) {
+ ContextCleanupInfo cci = new ContextCleanupInfo();
+ cci.context = context;
+ cci.who = who;
+ cci.what = what;
+ queueOrSendMessage(H.CLEAN_UP_CONTEXT, cci);
+ }
+
+ private final Activity performLaunchActivity(ActivityRecord r) {
+ // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
+
+ ActivityInfo aInfo = r.activityInfo;
+ if (r.packageInfo == null) {
+ r.packageInfo = getPackageInfo(aInfo.applicationInfo,
+ Context.CONTEXT_INCLUDE_CODE);
+ }
+
+ ComponentName component = r.intent.getComponent();
+ if (component == null) {
+ component = r.intent.resolveActivity(
+ mInitialApplication.getPackageManager());
+ r.intent.setComponent(component);
+ }
+
+ if (r.activityInfo.targetActivity != null) {
+ component = new ComponentName(r.activityInfo.packageName,
+ r.activityInfo.targetActivity);
+ }
+
+ Activity activity = null;
+ try {
+ java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
+ activity = mInstrumentation.newActivity(
+ cl, component.getClassName(), r.intent);
+ r.intent.setExtrasClassLoader(cl);
+ if (r.state != null) {
+ r.state.setClassLoader(cl);
+ }
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(activity, e)) {
+ throw new RuntimeException(
+ "Unable to instantiate activity " + component
+ + ": " + e.toString(), e);
+ }
+ }
+
+ try {
+ Application app = r.packageInfo.makeApplication();
+
+ if (localLOGV) Log.v(TAG, "Performing launch of " + r);
+ if (localLOGV) Log.v(
+ TAG, r + ": app=" + app
+ + ", appName=" + app.getPackageName()
+ + ", pkg=" + r.packageInfo.getPackageName()
+ + ", comp=" + r.intent.getComponent().toShortString()
+ + ", dir=" + r.packageInfo.getAppDir());
+
+ if (activity != null) {
+ ApplicationContext appContext = new ApplicationContext();
+ appContext.init(r.packageInfo, r.token, this);
+ appContext.setOuterContext(activity);
+ CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
+ Configuration config = new Configuration(mConfiguration);
+ activity.attach(appContext, this, getInstrumentation(), r.token, app,
+ r.intent, r.activityInfo, title, r.parent, r.embeddedID,
+ r.lastNonConfigurationInstance, r.lastNonConfigurationChildInstances,
+ config);
+
+ r.lastNonConfigurationInstance = null;
+ r.lastNonConfigurationChildInstances = null;
+ activity.mStartedActivity = false;
+ int theme = r.activityInfo.getThemeResource();
+ if (theme != 0) {
+ activity.setTheme(theme);
+ }
+
+ activity.mCalled = false;
+ mInstrumentation.callActivityOnCreate(activity, r.state);
+ if (!activity.mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + r.intent.getComponent().toShortString() +
+ " did not call through to super.onCreate()");
+ }
+ r.activity = activity;
+ r.stopped = true;
+ if (!r.activity.mFinished) {
+ activity.performStart();
+ r.stopped = false;
+ }
+ if (!r.activity.mFinished) {
+ if (r.state != null) {
+ mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
+ }
+ }
+ if (!r.activity.mFinished) {
+ activity.mCalled = false;
+ mInstrumentation.callActivityOnPostCreate(activity, r.state);
+ if (!activity.mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + r.intent.getComponent().toShortString() +
+ " did not call through to super.onPostCreate()");
+ }
+ }
+ r.state = null;
+ }
+ r.paused = true;
+
+ mActivities.put(r.token, r);
+
+ } catch (SuperNotCalledException e) {
+ throw e;
+
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(activity, e)) {
+ throw new RuntimeException(
+ "Unable to start activity " + component
+ + ": " + e.toString(), e);
+ }
+ }
+
+ return activity;
+ }
+
+ private final void handleLaunchActivity(ActivityRecord r) {
+ // If we are getting ready to gc after going to the background, well
+ // we are back active so skip it.
+ unscheduleGcIdler();
+
+ if (localLOGV) Log.v(
+ TAG, "Handling launch of " + r);
+ Activity a = performLaunchActivity(r);
+
+ if (a != null) {
+ handleResumeActivity(r.token, false, r.isForward);
+
+ if (!r.activity.mFinished && r.startsNotResumed) {
+ // The activity manager actually wants this one to start out
+ // paused, because it needs to be visible but isn't in the
+ // foreground. We accomplish this by going through the
+ // normal startup (because activities expect to go through
+ // onResume() the first time they run, before their window
+ // is displayed), and then pausing it. However, in this case
+ // we do -not- need to do the full pause cycle (of freezing
+ // and such) because the activity manager assumes it can just
+ // retain the current state it has.
+ try {
+ r.activity.mCalled = false;
+ mInstrumentation.callActivityOnPause(r.activity);
+ if (!r.activity.mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + r.intent.getComponent().toShortString() +
+ " did not call through to super.onPause()");
+ }
+
+ } catch (SuperNotCalledException e) {
+ throw e;
+
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to pause activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ r.paused = true;
+ }
+ } else {
+ // If there was an error, for any reason, tell the activity
+ // manager to stop us.
+ try {
+ ActivityManagerNative.getDefault()
+ .finishActivity(r.token, Activity.RESULT_CANCELED, null);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ private final void deliverNewIntents(ActivityRecord r,
+ List<Intent> intents) {
+ final int N = intents.size();
+ for (int i=0; i<N; i++) {
+ Intent intent = intents.get(i);
+ intent.setExtrasClassLoader(r.activity.getClassLoader());
+ mInstrumentation.callActivityOnNewIntent(r.activity, intent);
+ }
+ }
+
+ public final void performNewIntents(IBinder token,
+ List<Intent> intents) {
+ ActivityRecord r = mActivities.get(token);
+ if (r != null) {
+ final boolean resumed = !r.paused;
+ if (resumed) {
+ mInstrumentation.callActivityOnPause(r.activity);
+ }
+ deliverNewIntents(r, intents);
+ if (resumed) {
+ mInstrumentation.callActivityOnResume(r.activity);
+ }
+ }
+ }
+
+ private final void handleNewIntent(NewIntentData data) {
+ performNewIntents(data.token, data.intents);
+ }
+
+ private final void handleReceiver(ReceiverData data) {
+ // If we are getting ready to gc after going to the background, well
+ // we are back active so skip it.
+ unscheduleGcIdler();
+
+ String component = data.intent.getComponent().getClassName();
+
+ PackageInfo packageInfo = getPackageInfoNoCheck(
+ data.info.applicationInfo);
+
+ IActivityManager mgr = ActivityManagerNative.getDefault();
+
+ BroadcastReceiver receiver = null;
+ try {
+ java.lang.ClassLoader cl = packageInfo.getClassLoader();
+ data.intent.setExtrasClassLoader(cl);
+ if (data.resultExtras != null) {
+ data.resultExtras.setClassLoader(cl);
+ }
+ receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
+ } catch (Exception e) {
+ try {
+ mgr.finishReceiver(mAppThread.asBinder(), data.resultCode,
+ data.resultData, data.resultExtras, data.resultAbort);
+ } catch (RemoteException ex) {
+ }
+ throw new RuntimeException(
+ "Unable to instantiate receiver " + component
+ + ": " + e.toString(), e);
+ }
+
+ try {
+ Application app = packageInfo.makeApplication();
+
+ if (localLOGV) Log.v(
+ TAG, "Performing receive of " + data.intent
+ + ": app=" + app
+ + ", appName=" + app.getPackageName()
+ + ", pkg=" + packageInfo.getPackageName()
+ + ", comp=" + data.intent.getComponent().toShortString()
+ + ", dir=" + packageInfo.getAppDir());
+
+ ApplicationContext context = (ApplicationContext)app.getBaseContext();
+ receiver.setOrderedHint(true);
+ receiver.setResult(data.resultCode, data.resultData,
+ data.resultExtras);
+ receiver.setOrderedHint(data.sync);
+ receiver.onReceive(context.getReceiverRestrictedContext(),
+ data.intent);
+ } catch (Exception e) {
+ try {
+ mgr.finishReceiver(mAppThread.asBinder(), data.resultCode,
+ data.resultData, data.resultExtras, data.resultAbort);
+ } catch (RemoteException ex) {
+ }
+ if (!mInstrumentation.onException(receiver, e)) {
+ throw new RuntimeException(
+ "Unable to start receiver " + component
+ + ": " + e.toString(), e);
+ }
+ }
+
+ try {
+ if (data.sync) {
+ mgr.finishReceiver(
+ mAppThread.asBinder(), receiver.getResultCode(),
+ receiver.getResultData(), receiver.getResultExtras(false),
+ receiver.getAbortBroadcast());
+ } else {
+ mgr.finishReceiver(mAppThread.asBinder(), 0, null, null, false);
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ private final void handleCreateService(CreateServiceData data) {
+ // If we are getting ready to gc after going to the background, well
+ // we are back active so skip it.
+ unscheduleGcIdler();
+
+ PackageInfo packageInfo = getPackageInfoNoCheck(
+ data.info.applicationInfo);
+ Service service = null;
+ try {
+ java.lang.ClassLoader cl = packageInfo.getClassLoader();
+ service = (Service) cl.loadClass(data.info.name).newInstance();
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(service, e)) {
+ throw new RuntimeException(
+ "Unable to instantiate service " + data.info.name
+ + ": " + e.toString(), e);
+ }
+ }
+
+ try {
+ if (localLOGV) Log.v(TAG, "Creating service " + data.info.name);
+
+ ApplicationContext context = new ApplicationContext();
+ context.init(packageInfo, null, this);
+
+ Application app = packageInfo.makeApplication();
+ context.setOuterContext(service);
+ service.attach(context, this, data.info.name, data.token, app,
+ ActivityManagerNative.getDefault());
+ service.onCreate();
+ mServices.put(data.token, service);
+ try {
+ ActivityManagerNative.getDefault().serviceDoneExecuting(data.token);
+ } catch (RemoteException e) {
+ // nothing to do.
+ }
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(service, e)) {
+ throw new RuntimeException(
+ "Unable to create service " + data.info.name
+ + ": " + e.toString(), e);
+ }
+ }
+ }
+
+ private final void handleBindService(BindServiceData data) {
+ Service s = mServices.get(data.token);
+ if (s != null) {
+ try {
+ data.intent.setExtrasClassLoader(s.getClassLoader());
+ try {
+ if (!data.rebind) {
+ IBinder binder = s.onBind(data.intent);
+ ActivityManagerNative.getDefault().publishService(
+ data.token, data.intent, binder);
+ } else {
+ s.onRebind(data.intent);
+ ActivityManagerNative.getDefault().serviceDoneExecuting(
+ data.token);
+ }
+ } catch (RemoteException ex) {
+ }
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(s, e)) {
+ throw new RuntimeException(
+ "Unable to bind to service " + s
+ + " with " + data.intent + ": " + e.toString(), e);
+ }
+ }
+ }
+ }
+
+ private final void handleUnbindService(BindServiceData data) {
+ Service s = mServices.get(data.token);
+ if (s != null) {
+ try {
+ data.intent.setExtrasClassLoader(s.getClassLoader());
+ boolean doRebind = s.onUnbind(data.intent);
+ try {
+ if (doRebind) {
+ ActivityManagerNative.getDefault().unbindFinished(
+ data.token, data.intent, doRebind);
+ } else {
+ ActivityManagerNative.getDefault().serviceDoneExecuting(
+ data.token);
+ }
+ } catch (RemoteException ex) {
+ }
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(s, e)) {
+ throw new RuntimeException(
+ "Unable to unbind to service " + s
+ + " with " + data.intent + ": " + e.toString(), e);
+ }
+ }
+ }
+ }
+
+ private void handleDumpService(DumpServiceInfo info) {
+ try {
+ Service s = mServices.get(info.service);
+ if (s != null) {
+ PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd));
+ s.dump(info.fd, pw, info.args);
+ pw.close();
+ }
+ } finally {
+ synchronized (info) {
+ info.dumped = true;
+ info.notifyAll();
+ }
+ }
+ }
+
+ private final void handleServiceArgs(ServiceArgsData data) {
+ Service s = mServices.get(data.token);
+ if (s != null) {
+ try {
+ if (data.args != null) {
+ data.args.setExtrasClassLoader(s.getClassLoader());
+ }
+ s.onStart(data.args, data.startId);
+ try {
+ ActivityManagerNative.getDefault().serviceDoneExecuting(data.token);
+ } catch (RemoteException e) {
+ // nothing to do.
+ }
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(s, e)) {
+ throw new RuntimeException(
+ "Unable to start service " + s
+ + " with " + data.args + ": " + e.toString(), e);
+ }
+ }
+ }
+ }
+
+ private final void handleStopService(IBinder token) {
+ Service s = mServices.remove(token);
+ if (s != null) {
+ try {
+ if (localLOGV) Log.v(TAG, "Destroying service " + s);
+ s.onDestroy();
+ Context context = s.getBaseContext();
+ if (context instanceof ApplicationContext) {
+ final String who = s.getClassName();
+ ((ApplicationContext) context).scheduleFinalCleanup(who, "Service");
+ }
+ try {
+ ActivityManagerNative.getDefault().serviceDoneExecuting(token);
+ } catch (RemoteException e) {
+ // nothing to do.
+ }
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(s, e)) {
+ throw new RuntimeException(
+ "Unable to stop service " + s
+ + ": " + e.toString(), e);
+ }
+ }
+ }
+ //Log.i(TAG, "Running services: " + mServices);
+ }
+
+ public final ActivityRecord performResumeActivity(IBinder token,
+ boolean clearHide) {
+ ActivityRecord r = mActivities.get(token);
+ if (localLOGV) Log.v(TAG, "Performing resume of " + r
+ + " finished=" + r.activity.mFinished);
+ if (r != null && !r.activity.mFinished) {
+ if (clearHide) {
+ r.hideForNow = false;
+ r.activity.mStartedActivity = false;
+ }
+ try {
+ if (r.pendingIntents != null) {
+ deliverNewIntents(r, r.pendingIntents);
+ r.pendingIntents = null;
+ }
+ if (r.pendingResults != null) {
+ deliverResults(r, r.pendingResults);
+ r.pendingResults = null;
+ }
+ r.activity.performResume();
+
+ EventLog.writeEvent(LOG_ON_RESUME_CALLED,
+ r.activity.getComponentName().getClassName());
+
+ r.paused = false;
+ r.stopped = false;
+ if (r.activity.mStartedActivity) {
+ r.hideForNow = true;
+ }
+ r.state = null;
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to resume activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ }
+ return r;
+ }
+
+ final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
+ // If we are getting ready to gc after going to the background, well
+ // we are back active so skip it.
+ unscheduleGcIdler();
+
+ ActivityRecord r = performResumeActivity(token, clearHide);
+
+ if (r != null) {
+ final Activity a = r.activity;
+
+ if (localLOGV) Log.v(
+ TAG, "Resume " + r + " started activity: " +
+ a.mStartedActivity + ", hideForNow: " + r.hideForNow
+ + ", finished: " + a.mFinished);
+
+ final int forwardBit = isForward ?
+ WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
+
+ // If the window hasn't yet been added to the window manager,
+ // and this guy didn't finish itself or start another activity,
+ // then go ahead and add the window.
+ if (r.window == null && !a.mFinished && !a.mStartedActivity) {
+ r.window = r.activity.getWindow();
+ View decor = r.window.getDecorView();
+ decor.setVisibility(View.INVISIBLE);
+ ViewManager wm = a.getWindowManager();
+ WindowManager.LayoutParams l = r.window.getAttributes();
+ a.mDecor = decor;
+ l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+ l.softInputMode |= forwardBit;
+ if (a.mVisibleFromClient) {
+ a.mWindowAdded = true;
+ wm.addView(decor, l);
+ }
+
+ // If the window has already been added, but during resume
+ // we started another activity, then don't yet make the
+ // window visisble.
+ } else if (a.mStartedActivity) {
+ if (localLOGV) Log.v(
+ TAG, "Launch " + r + " mStartedActivity set");
+ r.hideForNow = true;
+ }
+
+ // The window is now visible if it has been added, we are not
+ // simply finishing, and we are not starting another activity.
+ if (!r.activity.mFinished && r.activity.mDecor != null
+ && !r.hideForNow) {
+ if (r.newConfig != null) {
+ performConfigurationChanged(r.activity, r.newConfig);
+ r.newConfig = null;
+ }
+ if (localLOGV) Log.v(TAG, "Resuming " + r + " with isForward="
+ + isForward);
+ WindowManager.LayoutParams l = r.window.getAttributes();
+ if ((l.softInputMode
+ & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
+ != forwardBit) {
+ l.softInputMode = (l.softInputMode
+ & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
+ | forwardBit;
+ ViewManager wm = a.getWindowManager();
+ View decor = r.window.getDecorView();
+ wm.updateViewLayout(decor, l);
+ }
+ r.activity.mVisibleFromServer = true;
+ mNumVisibleActivities++;
+ if (r.activity.mVisibleFromClient) {
+ r.activity.makeVisible();
+ }
+ }
+
+ r.nextIdle = mNewActivities;
+ mNewActivities = r;
+ if (localLOGV) Log.v(
+ TAG, "Scheduling idle handler for " + r);
+ Looper.myQueue().addIdleHandler(new Idler());
+
+ } else {
+ // If an exception was thrown when trying to resume, then
+ // just end this activity.
+ try {
+ ActivityManagerNative.getDefault()
+ .finishActivity(token, Activity.RESULT_CANCELED, null);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ private int mThumbnailWidth = -1;
+ private int mThumbnailHeight = -1;
+
+ private final Bitmap createThumbnailBitmap(ActivityRecord r) {
+ Bitmap thumbnail = null;
+ try {
+ int w = mThumbnailWidth;
+ int h;
+ if (w < 0) {
+ Resources res = r.activity.getResources();
+ mThumbnailHeight = h =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+
+ mThumbnailWidth = w =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+ } else {
+ h = mThumbnailHeight;
+ }
+
+ // XXX Only set hasAlpha if needed?
+ thumbnail = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
+ thumbnail.eraseColor(0);
+ Canvas cv = new Canvas(thumbnail);
+ if (!r.activity.onCreateThumbnail(thumbnail, cv)) {
+ thumbnail = null;
+ }
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to create thumbnail of "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ thumbnail = null;
+ }
+
+ return thumbnail;
+ }
+
+ private final void handlePauseActivity(IBinder token, boolean finished,
+ boolean userLeaving, int configChanges) {
+ ActivityRecord r = mActivities.get(token);
+ if (r != null) {
+ //Log.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
+ if (userLeaving) {
+ performUserLeavingActivity(r);
+ }
+
+ r.activity.mConfigChangeFlags |= configChanges;
+ Bundle state = performPauseActivity(token, finished, true);
+
+ // Tell the activity manager we have paused.
+ try {
+ ActivityManagerNative.getDefault().activityPaused(token, state);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ final void performUserLeavingActivity(ActivityRecord r) {
+ mInstrumentation.callActivityOnUserLeaving(r.activity);
+ }
+
+ final Bundle performPauseActivity(IBinder token, boolean finished,
+ boolean saveState) {
+ ActivityRecord r = mActivities.get(token);
+ return r != null ? performPauseActivity(r, finished, saveState) : null;
+ }
+
+ final Bundle performPauseActivity(ActivityRecord r, boolean finished,
+ boolean saveState) {
+ if (r.paused) {
+ if (r.activity.mFinished) {
+ // If we are finishing, we won't call onResume() in certain cases.
+ // So here we likewise don't want to call onPause() if the activity
+ // isn't resumed.
+ return null;
+ }
+ RuntimeException e = new RuntimeException(
+ "Performing pause of activity that is not resumed: "
+ + r.intent.getComponent().toShortString());
+ Log.e(TAG, e.getMessage(), e);
+ }
+ Bundle state = null;
+ if (finished) {
+ r.activity.mFinished = true;
+ }
+ try {
+ // Next have the activity save its current state and managed dialogs...
+ if (!r.activity.mFinished && saveState) {
+ state = new Bundle();
+ mInstrumentation.callActivityOnSaveInstanceState(r.activity, state);
+ r.state = state;
+ }
+ // Now we are idle.
+ r.activity.mCalled = false;
+ mInstrumentation.callActivityOnPause(r.activity);
+ EventLog.writeEvent(LOG_ON_PAUSE_CALLED, r.activity.getComponentName().getClassName());
+ if (!r.activity.mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + r.intent.getComponent().toShortString() +
+ " did not call through to super.onPause()");
+ }
+
+ } catch (SuperNotCalledException e) {
+ throw e;
+
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to pause activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ r.paused = true;
+ return state;
+ }
+
+ final void performStopActivity(IBinder token) {
+ ActivityRecord r = mActivities.get(token);
+ performStopActivityInner(r, null, false);
+ }
+
+ private static class StopInfo {
+ Bitmap thumbnail;
+ CharSequence description;
+ }
+
+ private final class ProviderRefCount {
+ public int count;
+ ProviderRefCount(int pCount) {
+ count = pCount;
+ }
+ }
+
+ private final void performStopActivityInner(ActivityRecord r,
+ StopInfo info, boolean keepShown) {
+ if (localLOGV) Log.v(TAG, "Performing stop of " + r);
+ if (r != null) {
+ if (!keepShown && r.stopped) {
+ if (r.activity.mFinished) {
+ // If we are finishing, we won't call onResume() in certain
+ // cases. So here we likewise don't want to call onStop()
+ // if the activity isn't resumed.
+ return;
+ }
+ RuntimeException e = new RuntimeException(
+ "Performing stop of activity that is not resumed: "
+ + r.intent.getComponent().toShortString());
+ Log.e(TAG, e.getMessage(), e);
+ }
+
+ if (info != null) {
+ try {
+ // First create a thumbnail for the activity...
+ //info.thumbnail = createThumbnailBitmap(r);
+ info.description = r.activity.onCreateDescription();
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to save state of activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ }
+
+ if (!keepShown) {
+ try {
+ // Now we are idle.
+ r.activity.performStop();
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to stop activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ r.stopped = true;
+ }
+
+ r.paused = true;
+ }
+ }
+
+ private final void updateVisibility(ActivityRecord r, boolean show) {
+ View v = r.activity.mDecor;
+ if (v != null) {
+ if (show) {
+ if (!r.activity.mVisibleFromServer) {
+ r.activity.mVisibleFromServer = true;
+ mNumVisibleActivities++;
+ if (r.activity.mVisibleFromClient) {
+ r.activity.makeVisible();
+ }
+ }
+ if (r.newConfig != null) {
+ performConfigurationChanged(r.activity, r.newConfig);
+ r.newConfig = null;
+ }
+ } else {
+ if (r.activity.mVisibleFromServer) {
+ r.activity.mVisibleFromServer = false;
+ mNumVisibleActivities--;
+ v.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+ }
+
+ private final void handleStopActivity(IBinder token, boolean show, int configChanges) {
+ ActivityRecord r = mActivities.get(token);
+ r.activity.mConfigChangeFlags |= configChanges;
+
+ StopInfo info = new StopInfo();
+ performStopActivityInner(r, info, show);
+
+ if (localLOGV) Log.v(
+ TAG, "Finishing stop of " + r + ": show=" + show
+ + " win=" + r.window);
+
+ updateVisibility(r, show);
+
+ // Tell activity manager we have been stopped.
+ try {
+ ActivityManagerNative.getDefault().activityStopped(
+ r.token, info.thumbnail, info.description);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ final void performRestartActivity(IBinder token) {
+ ActivityRecord r = mActivities.get(token);
+ if (r.stopped) {
+ r.activity.performRestart();
+ r.stopped = false;
+ }
+ }
+
+ private final void handleWindowVisibility(IBinder token, boolean show) {
+ ActivityRecord r = mActivities.get(token);
+ if (!show && !r.stopped) {
+ performStopActivityInner(r, null, show);
+ } else if (show && r.stopped) {
+ // If we are getting ready to gc after going to the background, well
+ // we are back active so skip it.
+ unscheduleGcIdler();
+
+ r.activity.performRestart();
+ r.stopped = false;
+ }
+ if (r.activity.mDecor != null) {
+ if (Config.LOGV) Log.v(
+ TAG, "Handle window " + r + " visibility: " + show);
+ updateVisibility(r, show);
+ }
+ }
+
+ private final void deliverResults(ActivityRecord r, List<ResultInfo> results) {
+ final int N = results.size();
+ for (int i=0; i<N; i++) {
+ ResultInfo ri = results.get(i);
+ try {
+ if (ri.mData != null) {
+ ri.mData.setExtrasClassLoader(r.activity.getClassLoader());
+ }
+ r.activity.dispatchActivityResult(ri.mResultWho,
+ ri.mRequestCode, ri.mResultCode, ri.mData);
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Failure delivering result " + ri + " to activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ }
+ }
+
+ private final void handleSendResult(ResultData res) {
+ ActivityRecord r = mActivities.get(res.token);
+ if (localLOGV) Log.v(TAG, "Handling send result to " + r);
+ if (r != null) {
+ final boolean resumed = !r.paused;
+ if (!r.activity.mFinished && r.activity.mDecor != null
+ && r.hideForNow && resumed) {
+ // We had hidden the activity because it started another
+ // one... we have gotten a result back and we are not
+ // paused, so make sure our window is visible.
+ updateVisibility(r, true);
+ }
+ if (resumed) {
+ try {
+ // Now we are idle.
+ r.activity.mCalled = false;
+ mInstrumentation.callActivityOnPause(r.activity);
+ if (!r.activity.mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + r.intent.getComponent().toShortString()
+ + " did not call through to super.onPause()");
+ }
+ } catch (SuperNotCalledException e) {
+ throw e;
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to pause activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ }
+ deliverResults(r, res.results);
+ if (resumed) {
+ mInstrumentation.callActivityOnResume(r.activity);
+ }
+ }
+ }
+
+ public final ActivityRecord performDestroyActivity(IBinder token, boolean finishing) {
+ return performDestroyActivity(token, finishing, 0, false);
+ }
+
+ private final ActivityRecord performDestroyActivity(IBinder token, boolean finishing,
+ int configChanges, boolean getNonConfigInstance) {
+ ActivityRecord r = mActivities.get(token);
+ if (localLOGV) Log.v(TAG, "Performing finish of " + r);
+ if (r != null) {
+ r.activity.mConfigChangeFlags |= configChanges;
+ if (finishing) {
+ r.activity.mFinished = true;
+ }
+ if (!r.paused) {
+ try {
+ r.activity.mCalled = false;
+ mInstrumentation.callActivityOnPause(r.activity);
+ EventLog.writeEvent(LOG_ON_PAUSE_CALLED,
+ r.activity.getComponentName().getClassName());
+ if (!r.activity.mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + r.intent.getComponent().toShortString()
+ + " did not call through to super.onPause()");
+ }
+ } catch (SuperNotCalledException e) {
+ throw e;
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to pause activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ r.paused = true;
+ }
+ if (!r.stopped) {
+ try {
+ r.activity.performStop();
+ } catch (SuperNotCalledException e) {
+ throw e;
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to stop activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ r.stopped = true;
+ }
+ if (getNonConfigInstance) {
+ try {
+ r.lastNonConfigurationInstance
+ = r.activity.onRetainNonConfigurationInstance();
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to retain activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ try {
+ r.lastNonConfigurationChildInstances
+ = r.activity.onRetainNonConfigurationChildInstances();
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to retain child activities "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+
+ }
+ try {
+ r.activity.mCalled = false;
+ r.activity.onDestroy();
+ if (!r.activity.mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + r.intent.getComponent().toShortString() +
+ " did not call through to super.onDestroy()");
+ }
+ if (r.window != null) {
+ r.window.closeAllPanels();
+ }
+ } catch (SuperNotCalledException e) {
+ throw e;
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to destroy activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ }
+ mActivities.remove(token);
+
+ return r;
+ }
+
+ private final void handleDestroyActivity(IBinder token, boolean finishing,
+ int configChanges, boolean getNonConfigInstance) {
+ ActivityRecord r = performDestroyActivity(token, finishing,
+ configChanges, getNonConfigInstance);
+ if (r != null) {
+ WindowManager wm = r.activity.getWindowManager();
+ View v = r.activity.mDecor;
+ if (v != null) {
+ if (r.activity.mVisibleFromServer) {
+ mNumVisibleActivities--;
+ }
+ IBinder wtoken = v.getWindowToken();
+ if (r.activity.mWindowAdded) {
+ wm.removeViewImmediate(v);
+ }
+ if (wtoken != null) {
+ WindowManagerImpl.getDefault().closeAll(wtoken,
+ r.activity.getClass().getName(), "Activity");
+ }
+ r.activity.mDecor = null;
+ }
+ WindowManagerImpl.getDefault().closeAll(token,
+ r.activity.getClass().getName(), "Activity");
+
+ // Mocked out contexts won't be participating in the normal
+ // process lifecycle, but if we're running with a proper
+ // ApplicationContext we need to have it tear down things
+ // cleanly.
+ Context c = r.activity.getBaseContext();
+ if (c instanceof ApplicationContext) {
+ ((ApplicationContext) c).scheduleFinalCleanup(
+ r.activity.getClass().getName(), "Activity");
+ }
+ }
+ if (finishing) {
+ try {
+ ActivityManagerNative.getDefault().activityDestroyed(token);
+ } catch (RemoteException ex) {
+ // If the system process has died, it's game over for everyone.
+ }
+ }
+ }
+
+ private final void handleRelaunchActivity(ActivityRecord tmp, int configChanges) {
+ // If we are getting ready to gc after going to the background, well
+ // we are back active so skip it.
+ unscheduleGcIdler();
+
+ Configuration changedConfig = null;
+
+ // First: make sure we have the most recent configuration and most
+ // recent version of the activity, or skip it if some previous call
+ // had taken a more recent version.
+ synchronized (mRelaunchingActivities) {
+ int N = mRelaunchingActivities.size();
+ IBinder token = tmp.token;
+ tmp = null;
+ for (int i=0; i<N; i++) {
+ ActivityRecord r = mRelaunchingActivities.get(i);
+ if (r.token == token) {
+ tmp = r;
+ mRelaunchingActivities.remove(i);
+ i--;
+ N--;
+ }
+ }
+
+ if (tmp == null) {
+ return;
+ }
+
+ if (mPendingConfiguration != null) {
+ changedConfig = mPendingConfiguration;
+ mPendingConfiguration = null;
+ }
+ }
+
+ // If there was a pending configuration change, execute it first.
+ if (changedConfig != null) {
+ handleConfigurationChanged(changedConfig);
+ }
+
+ ActivityRecord r = mActivities.get(tmp.token);
+ if (localLOGV) Log.v(TAG, "Handling relaunch of " + r);
+ if (r == null) {
+ return;
+ }
+
+ r.activity.mConfigChangeFlags |= configChanges;
+
+ Bundle savedState = null;
+ if (!r.paused) {
+ savedState = performPauseActivity(r.token, false, true);
+ }
+
+ handleDestroyActivity(r.token, false, configChanges, true);
+
+ r.activity = null;
+ r.window = null;
+ r.hideForNow = false;
+ r.nextIdle = null;
+ r.pendingResults = tmp.pendingResults;
+ r.pendingIntents = tmp.pendingIntents;
+ r.startsNotResumed = tmp.startsNotResumed;
+ if (savedState != null) {
+ r.state = savedState;
+ }
+
+ handleLaunchActivity(r);
+ }
+
+ private final void handleRequestThumbnail(IBinder token) {
+ ActivityRecord r = mActivities.get(token);
+ Bitmap thumbnail = createThumbnailBitmap(r);
+ CharSequence description = null;
+ try {
+ description = r.activity.onCreateDescription();
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to create description of activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
+ }
+ }
+ //System.out.println("Reporting top thumbnail " + thumbnail);
+ try {
+ ActivityManagerNative.getDefault().reportThumbnail(
+ token, thumbnail, description);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ ArrayList<ComponentCallbacks> collectComponentCallbacksLocked(
+ boolean allActivities, Configuration newConfig) {
+ ArrayList<ComponentCallbacks> callbacks
+ = new ArrayList<ComponentCallbacks>();
+
+ if (mActivities.size() > 0) {
+ Iterator<ActivityRecord> it = mActivities.values().iterator();
+ while (it.hasNext()) {
+ ActivityRecord ar = it.next();
+ Activity a = ar.activity;
+ if (a != null) {
+ if (!ar.activity.mFinished && (allActivities ||
+ (a != null && !ar.paused))) {
+ // If the activity is currently resumed, its configuration
+ // needs to change right now.
+ callbacks.add(a);
+ } else if (newConfig != null) {
+ // Otherwise, we will tell it about the change
+ // the next time it is resumed or shown. Note that
+ // the activity manager may, before then, decide the
+ // activity needs to be destroyed to handle its new
+ // configuration.
+ ar.newConfig = newConfig;
+ }
+ }
+ }
+ }
+ if (mServices.size() > 0) {
+ Iterator<Service> it = mServices.values().iterator();
+ while (it.hasNext()) {
+ callbacks.add(it.next());
+ }
+ }
+ synchronized (mProviderMap) {
+ if (mLocalProviders.size() > 0) {
+ Iterator<ProviderRecord> it = mLocalProviders.values().iterator();
+ while (it.hasNext()) {
+ callbacks.add(it.next().mLocalProvider);
+ }
+ }
+ }
+ final int N = mAllApplications.size();
+ for (int i=0; i<N; i++) {
+ callbacks.add(mAllApplications.get(i));
+ }
+
+ return callbacks;
+ }
+
+ private final void performConfigurationChanged(
+ ComponentCallbacks cb, Configuration config) {
+ // Only for Activity objects, check that they actually call up to their
+ // superclass implementation. ComponentCallbacks is an interface, so
+ // we check the runtime type and act accordingly.
+ Activity activity = (cb instanceof Activity) ? (Activity) cb : null;
+ if (activity != null) {
+ activity.mCalled = false;
+ }
+
+ boolean shouldChangeConfig = false;
+ if ((activity == null) || (activity.mCurrentConfig == null)) {
+ shouldChangeConfig = true;
+ } else {
+
+ // If the new config is the same as the config this Activity
+ // is already running with then don't bother calling
+ // onConfigurationChanged
+ int diff = activity.mCurrentConfig.diff(config);
+ if (diff != 0) {
+
+ // If this activity doesn't handle any of the config changes
+ // then don't bother calling onConfigurationChanged as we're
+ // going to destroy it.
+ if ((~activity.mActivityInfo.configChanges & diff) == 0) {
+ shouldChangeConfig = true;
+ }
+ }
+ }
+
+ if (shouldChangeConfig) {
+ cb.onConfigurationChanged(config);
+
+ if (activity != null) {
+ if (!activity.mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + activity.getLocalClassName() +
+ " did not call through to super.onConfigurationChanged()");
+ }
+ activity.mConfigChangeFlags = 0;
+ activity.mCurrentConfig = new Configuration(config);
+ }
+ }
+ }
+
+ final void handleConfigurationChanged(Configuration config) {
+
+ synchronized (mRelaunchingActivities) {
+ if (mPendingConfiguration != null) {
+ config = mPendingConfiguration;
+ mPendingConfiguration = null;
+ }
+ }
+
+ ArrayList<ComponentCallbacks> callbacks
+ = new ArrayList<ComponentCallbacks>();
+
+ synchronized(mPackages) {
+ if (mConfiguration == null) {
+ mConfiguration = new Configuration();
+ }
+ mConfiguration.updateFrom(config);
+ DisplayMetrics dm = getDisplayMetricsLocked(true);
+
+ // set it for java, this also affects newly created Resources
+ if (config.locale != null) {
+ Locale.setDefault(config.locale);
+ }
+
+ Resources.updateSystemConfiguration(config, null);
+
+ ApplicationContext.ApplicationPackageManager.configurationChanged();
+ //Log.i(TAG, "Configuration changed in " + currentPackageName());
+ {
+ Iterator<WeakReference<Resources>> it =
+ mActiveResources.values().iterator();
+ //Iterator<Map.Entry<String, WeakReference<Resources>>> it =
+ // mActiveResources.entrySet().iterator();
+ while (it.hasNext()) {
+ WeakReference<Resources> v = it.next();
+ Resources r = v.get();
+ if (r != null) {
+ r.updateConfiguration(config, dm);
+ //Log.i(TAG, "Updated app resources " + v.getKey()
+ // + " " + r + ": " + r.getConfiguration());
+ } else {
+ //Log.i(TAG, "Removing old resources " + v.getKey());
+ it.remove();
+ }
+ }
+ }
+
+ callbacks = collectComponentCallbacksLocked(false, config);
+ }
+
+ final int N = callbacks.size();
+ for (int i=0; i<N; i++) {
+ performConfigurationChanged(callbacks.get(i), config);
+ }
+ }
+
+ final void handleActivityConfigurationChanged(IBinder token) {
+ ActivityRecord r = mActivities.get(token);
+ if (r == null || r.activity == null) {
+ return;
+ }
+
+ performConfigurationChanged(r.activity, mConfiguration);
+ }
+
+ final void handleLowMemory() {
+ ArrayList<ComponentCallbacks> callbacks
+ = new ArrayList<ComponentCallbacks>();
+
+ synchronized(mPackages) {
+ callbacks = collectComponentCallbacksLocked(true, null);
+ }
+
+ final int N = callbacks.size();
+ for (int i=0; i<N; i++) {
+ callbacks.get(i).onLowMemory();
+ }
+
+ // Ask SQLite to free up as much memory as it can, mostly from it's page caches
+ int sqliteReleased = SQLiteDatabase.releaseMemory();
+ EventLog.writeEvent(SQLITE_MEM_RELEASED_EVENT_LOG_TAG, sqliteReleased);
+
+ BinderInternal.forceGc("mem");
+ }
+
+ private final void handleBindApplication(AppBindData data) {
+ mBoundApplication = data;
+ mConfiguration = new Configuration(data.config);
+
+ // We now rely on this being set by zygote.
+ //Process.setGid(data.appInfo.gid);
+ //Process.setUid(data.appInfo.uid);
+
+ // send up app name; do this *before* waiting for debugger
+ android.ddm.DdmHandleAppName.setAppName(data.processName);
+
+ /*
+ * Before spawning a new process, reset the time zone to be the system time zone.
+ * This needs to be done because the system time zone could have changed after the
+ * the spawning of this process. Without doing this this process would have the incorrect
+ * system time zone.
+ */
+ TimeZone.setDefault(null);
+
+ /*
+ * Initialize the default locale in this process for the reasons we set the time zone.
+ */
+ Locale.setDefault(data.config.locale);
+
+ data.info = getPackageInfoNoCheck(data.appInfo);
+
+ if (data.debugMode != IApplicationThread.DEBUG_OFF) {
+ // XXX should have option to change the port.
+ Debug.changeDebugPort(8100);
+ if (data.debugMode == IApplicationThread.DEBUG_WAIT) {
+ Log.w(TAG, "Application " + data.info.getPackageName()
+ + " is waiting for the debugger on port 8100...");
+
+ IActivityManager mgr = ActivityManagerNative.getDefault();
+ try {
+ mgr.showWaitingForDebugger(mAppThread, true);
+ } catch (RemoteException ex) {
+ }
+
+ Debug.waitForDebugger();
+
+ try {
+ mgr.showWaitingForDebugger(mAppThread, false);
+ } catch (RemoteException ex) {
+ }
+
+ } else {
+ Log.w(TAG, "Application " + data.info.getPackageName()
+ + " can be debugged on port 8100...");
+ }
+ }
+
+ if (data.instrumentationName != null) {
+ ApplicationContext appContext = new ApplicationContext();
+ appContext.init(data.info, null, this);
+ InstrumentationInfo ii = null;
+ try {
+ ii = appContext.getPackageManager().
+ getInstrumentationInfo(data.instrumentationName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ if (ii == null) {
+ throw new RuntimeException(
+ "Unable to find instrumentation info for: "
+ + data.instrumentationName);
+ }
+
+ mInstrumentationAppDir = ii.sourceDir;
+ mInstrumentationAppPackage = ii.packageName;
+ mInstrumentedAppDir = data.info.getAppDir();
+
+ ApplicationInfo instrApp = new ApplicationInfo();
+ instrApp.packageName = ii.packageName;
+ instrApp.sourceDir = ii.sourceDir;
+ instrApp.publicSourceDir = ii.publicSourceDir;
+ instrApp.dataDir = ii.dataDir;
+ PackageInfo pi = getPackageInfo(instrApp,
+ appContext.getClassLoader(), false, true);
+ ApplicationContext instrContext = new ApplicationContext();
+ instrContext.init(pi, null, this);
+
+ try {
+ java.lang.ClassLoader cl = instrContext.getClassLoader();
+ mInstrumentation = (Instrumentation)
+ cl.loadClass(data.instrumentationName.getClassName()).newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Unable to instantiate instrumentation "
+ + data.instrumentationName + ": " + e.toString(), e);
+ }
+
+ mInstrumentation.init(this, instrContext, appContext,
+ new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher);
+
+ if (data.profileFile != null && !ii.handleProfiling) {
+ data.handlingProfiling = true;
+ File file = new File(data.profileFile);
+ file.getParentFile().mkdirs();
+ Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
+ }
+
+ try {
+ mInstrumentation.onCreate(data.instrumentationArgs);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(
+ "Exception thrown in onCreate() of "
+ + data.instrumentationName + ": " + e.toString(), e);
+ }
+
+ } else {
+ mInstrumentation = new Instrumentation();
+ }
+
+ Application app = data.info.makeApplication();
+ mInitialApplication = app;
+
+ List<ProviderInfo> providers = data.providers;
+ if (providers != null) {
+ installContentProviders(app, providers);
+ }
+
+ try {
+ mInstrumentation.callApplicationOnCreate(app);
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(app, e)) {
+ throw new RuntimeException(
+ "Unable to create application " + app.getClass().getName()
+ + ": " + e.toString(), e);
+ }
+ }
+ }
+
+ /*package*/ final void finishInstrumentation(int resultCode, Bundle results) {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ if (mBoundApplication.profileFile != null && mBoundApplication.handlingProfiling) {
+ Debug.stopMethodTracing();
+ }
+ //Log.i(TAG, "am: " + ActivityManagerNative.getDefault()
+ // + ", app thr: " + mAppThread);
+ try {
+ am.finishInstrumentation(mAppThread, resultCode, results);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ private final void installContentProviders(
+ Context context, List<ProviderInfo> providers) {
+ final ArrayList<IActivityManager.ContentProviderHolder> results =
+ new ArrayList<IActivityManager.ContentProviderHolder>();
+
+ Iterator<ProviderInfo> i = providers.iterator();
+ while (i.hasNext()) {
+ ProviderInfo cpi = i.next();
+ StringBuilder buf = new StringBuilder(128);
+ buf.append("Publishing provider ");
+ buf.append(cpi.authority);
+ buf.append(": ");
+ buf.append(cpi.name);
+ Log.i(TAG, buf.toString());
+ IContentProvider cp = installProvider(context, null, cpi, false);
+ if (cp != null) {
+ IActivityManager.ContentProviderHolder cph =
+ new IActivityManager.ContentProviderHolder(cpi);
+ cph.provider = cp;
+ results.add(cph);
+ // Don't ever unload this provider from the process.
+ synchronized(mProviderMap) {
+ mProviderRefCountMap.put(cp.asBinder(), new ProviderRefCount(10000));
+ }
+ }
+ }
+
+ try {
+ ActivityManagerNative.getDefault().publishContentProviders(
+ getApplicationThread(), results);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ private final IContentProvider getProvider(Context context, String name) {
+ synchronized(mProviderMap) {
+ final ProviderRecord pr = mProviderMap.get(name);
+ if (pr != null) {
+ return pr.mProvider;
+ }
+ }
+
+ IActivityManager.ContentProviderHolder holder = null;
+ try {
+ holder = ActivityManagerNative.getDefault().getContentProvider(
+ getApplicationThread(), name);
+ } catch (RemoteException ex) {
+ }
+ if (holder == null) {
+ Log.e(TAG, "Failed to find provider info for " + name);
+ return null;
+ }
+ if (holder.permissionFailure != null) {
+ throw new SecurityException("Permission " + holder.permissionFailure
+ + " required for provider " + name);
+ }
+
+ IContentProvider prov = installProvider(context, holder.provider,
+ holder.info, true);
+ //Log.i(TAG, "noReleaseNeeded=" + holder.noReleaseNeeded);
+ if (holder.noReleaseNeeded || holder.provider == null) {
+ // We are not going to release the provider if it is an external
+ // provider that doesn't care about being released, or if it is
+ // a local provider running in this process.
+ //Log.i(TAG, "*** NO RELEASE NEEDED");
+ synchronized(mProviderMap) {
+ mProviderRefCountMap.put(prov.asBinder(), new ProviderRefCount(10000));
+ }
+ }
+ return prov;
+ }
+
+ public final IContentProvider acquireProvider(Context c, String name) {
+ IContentProvider provider = getProvider(c, name);
+ if(provider == null)
+ return null;
+ IBinder jBinder = provider.asBinder();
+ synchronized(mProviderMap) {
+ ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
+ if(prc == null) {
+ mProviderRefCountMap.put(jBinder, new ProviderRefCount(1));
+ } else {
+ prc.count++;
+ } //end else
+ } //end synchronized
+ return provider;
+ }
+
+ public final boolean releaseProvider(IContentProvider provider) {
+ if(provider == null) {
+ return false;
+ }
+ IBinder jBinder = provider.asBinder();
+ synchronized(mProviderMap) {
+ ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
+ if(prc == null) {
+ if(localLOGV) Log.v(TAG, "releaseProvider::Weird shouldnt be here");
+ return false;
+ } else {
+ prc.count--;
+ if(prc.count == 0) {
+ mProviderRefCountMap.remove(jBinder);
+ //invoke removeProvider to dereference provider
+ removeProviderLocked(provider);
+ } //end if
+ } //end else
+ } //end synchronized
+ return true;
+ }
+
+ public final void removeProviderLocked(IContentProvider provider) {
+ if (provider == null) {
+ return;
+ }
+ IBinder providerBinder = provider.asBinder();
+ boolean amRemoveFlag = false;
+
+ // remove the provider from mProviderMap
+ Iterator<ProviderRecord> iter = mProviderMap.values().iterator();
+ while (iter.hasNext()) {
+ ProviderRecord pr = iter.next();
+ IBinder myBinder = pr.mProvider.asBinder();
+ if (myBinder == providerBinder) {
+ //find if its published by this process itself
+ if(pr.mLocalProvider != null) {
+ if(localLOGV) Log.i(TAG, "removeProvider::found local provider returning");
+ return;
+ }
+ if(localLOGV) Log.v(TAG, "removeProvider::Not local provider Unlinking " +
+ "death recipient");
+ //content provider is in another process
+ myBinder.unlinkToDeath(pr, 0);
+ iter.remove();
+ //invoke remove only once for the very first name seen
+ if(!amRemoveFlag) {
+ try {
+ if(localLOGV) Log.v(TAG, "removeProvider::Invoking " +
+ "ActivityManagerNative.removeContentProvider("+pr.mName);
+ ActivityManagerNative.getDefault().removeContentProvider(getApplicationThread(), pr.mName);
+ amRemoveFlag = true;
+ } catch (RemoteException e) {
+ //do nothing content provider object is dead any way
+ } //end catch
+ }
+ } //end if myBinder
+ } //end while iter
+ }
+
+ final void removeDeadProvider(String name, IContentProvider provider) {
+ synchronized(mProviderMap) {
+ ProviderRecord pr = mProviderMap.get(name);
+ if (pr.mProvider.asBinder() == provider.asBinder()) {
+ Log.i(TAG, "Removing dead content provider: " + name);
+ mProviderMap.remove(name);
+ }
+ }
+ }
+
+ final void removeDeadProviderLocked(String name, IContentProvider provider) {
+ ProviderRecord pr = mProviderMap.get(name);
+ if (pr.mProvider.asBinder() == provider.asBinder()) {
+ Log.i(TAG, "Removing dead content provider: " + name);
+ mProviderMap.remove(name);
+ }
+ }
+
+ private final IContentProvider installProvider(Context context,
+ IContentProvider provider, ProviderInfo info, boolean noisy) {
+ ContentProvider localProvider = null;
+ if (provider == null) {
+ if (noisy) {
+ Log.d(TAG, "Loading provider " + info.authority + ": "
+ + info.name);
+ }
+ Context c = null;
+ ApplicationInfo ai = info.applicationInfo;
+ if (context.getPackageName().equals(ai.packageName)) {
+ c = context;
+ } else if (mInitialApplication != null &&
+ mInitialApplication.getPackageName().equals(ai.packageName)) {
+ c = mInitialApplication;
+ } else {
+ try {
+ c = context.createPackageContext(ai.packageName,
+ Context.CONTEXT_INCLUDE_CODE);
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+ if (c == null) {
+ Log.w(TAG, "Unable to get context for package " +
+ ai.packageName +
+ " while loading content provider " +
+ info.name);
+ return null;
+ }
+ try {
+ final java.lang.ClassLoader cl = c.getClassLoader();
+ localProvider = (ContentProvider)cl.
+ loadClass(info.name).newInstance();
+ provider = localProvider.getIContentProvider();
+ if (provider == null) {
+ Log.e(TAG, "Failed to instantiate class " +
+ info.name + " from sourceDir " +
+ info.applicationInfo.sourceDir);
+ return null;
+ }
+ if (Config.LOGV) Log.v(
+ TAG, "Instantiating local provider " + info.name);
+ // XXX Need to create the correct context for this provider.
+ localProvider.attachInfo(c, info);
+ } catch (java.lang.Exception e) {
+ if (!mInstrumentation.onException(null, e)) {
+ throw new RuntimeException(
+ "Unable to get provider " + info.name
+ + ": " + e.toString(), e);
+ }
+ return null;
+ }
+ } else if (localLOGV) {
+ Log.v(TAG, "Installing external provider " + info.authority + ": "
+ + info.name);
+ }
+
+ synchronized (mProviderMap) {
+ // Cache the pointer for the remote provider.
+ String names[] = PATTERN_SEMICOLON.split(info.authority);
+ for (int i=0; i<names.length; i++) {
+ ProviderRecord pr = new ProviderRecord(names[i], provider,
+ localProvider);
+ try {
+ provider.asBinder().linkToDeath(pr, 0);
+ mProviderMap.put(names[i], pr);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+ if (localProvider != null) {
+ mLocalProviders.put(provider.asBinder(),
+ new ProviderRecord(null, provider, localProvider));
+ }
+ }
+
+ return provider;
+ }
+
+ private final void attach(boolean system) {
+ sThreadLocal.set(this);
+ mSystemThread = system;
+ AndroidHttpClient.setThreadBlocked(true);
+ if (!system) {
+ android.ddm.DdmHandleAppName.setAppName("<pre-initialized>");
+ RuntimeInit.setApplicationObject(mAppThread.asBinder());
+ IActivityManager mgr = ActivityManagerNative.getDefault();
+ try {
+ mgr.attachApplication(mAppThread);
+ } catch (RemoteException ex) {
+ }
+ } else {
+ // Don't set application object here -- if the system crashes,
+ // we can't display an alert, we just want to die die die.
+ android.ddm.DdmHandleAppName.setAppName("system_process");
+ try {
+ mInstrumentation = new Instrumentation();
+ ApplicationContext context = new ApplicationContext();
+ context.init(getSystemContext().mPackageInfo, null, this);
+ Application app = Instrumentation.newApplication(Application.class, context);
+ mAllApplications.add(app);
+ mInitialApplication = app;
+ app.onCreate();
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Unable to instantiate Application():" + e.toString(), e);
+ }
+ }
+ }
+
+ private final void detach()
+ {
+ AndroidHttpClient.setThreadBlocked(false);
+ sThreadLocal.set(null);
+ }
+
+ public static final ActivityThread systemMain() {
+ ActivityThread thread = new ActivityThread();
+ thread.attach(true);
+ return thread;
+ }
+
+ public final void installSystemProviders(List providers) {
+ if (providers != null) {
+ installContentProviders(mInitialApplication,
+ (List<ProviderInfo>)providers);
+ }
+ }
+
+ public static final void main(String[] args) {
+ Process.setArgV0("<pre-initialized>");
+
+ Looper.prepareMainLooper();
+
+ ActivityThread thread = new ActivityThread();
+ thread.attach(false);
+
+ Looper.loop();
+
+ if (Process.supportsProcesses()) {
+ throw new RuntimeException("Main thread loop unexpectedly exited");
+ }
+
+ thread.detach();
+ String name;
+ if (thread.mInitialApplication != null) name = thread.mInitialApplication.getPackageName();
+ else name = "<unknown>";
+ Log.i(TAG, "Main thread of " + name + " is now exiting");
+ }
+}
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
new file mode 100644
index 0000000..b4c0e31
--- /dev/null
+++ b/core/java/android/app/AlarmManager.java
@@ -0,0 +1,276 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * This class provides access to the system alarm services. These allow you
+ * to schedule your application to be run at some point in the future. When
+ * an alarm goes off, the {@link Intent} that had been registered for it
+ * is broadcast by the system, automatically starting the target application
+ * if it is not already running. Registered alarms are retained while the
+ * device is asleep (and can optionally wake the device up if they go off
+ * during that time), but will be cleared if it is turned off and rebooted.
+ *
+ * <p><b>Note: The Alarm Manager is intended for cases where you want to have
+ * your application code run at a specific time, even if your application is
+ * not currently running. For normal timing operations (ticks, timeouts,
+ * etc) it is easier and much more efficient to use
+ * {@link android.os.Handler}.</b>
+ *
+ * <p>You do not
+ * instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.ALARM_SERVICE)}.
+ */
+public class AlarmManager
+{
+ /**
+ * Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()}
+ * (wall clock time in UTC), which will wake up the device when
+ * it goes off.
+ */
+ public static final int RTC_WAKEUP = 0;
+ /**
+ * Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()}
+ * (wall clock time in UTC). This alarm does not wake the
+ * device up; if it goes off while the device is asleep, it will not be
+ * delivered until the next time the device wakes up.
+ */
+ public static final int RTC = 1;
+ /**
+ * Alarm time in {@link android.os.SystemClock#elapsedRealtime
+ * SystemClock.elapsedRealtime()} (time since boot, including sleep),
+ * which will wake up the device when it goes off.
+ */
+ public static final int ELAPSED_REALTIME_WAKEUP = 2;
+ /**
+ * Alarm time in {@link android.os.SystemClock#elapsedRealtime
+ * SystemClock.elapsedRealtime()} (time since boot, including sleep).
+ * This alarm does not wake the device up; if it goes off while the device
+ * is asleep, it will not be delivered until the next time the device
+ * wakes up.
+ */
+ public static final int ELAPSED_REALTIME = 3;
+
+ private final IAlarmManager mService;
+
+ /**
+ * package private on purpose
+ */
+ AlarmManager(IAlarmManager service) {
+ mService = service;
+ }
+
+ /**
+ * Schedule an alarm. <b>Note: for timing operations (ticks, timeouts,
+ * etc) it is easier and much more efficient to use
+ * {@link android.os.Handler}.</b> If there is already an alarm scheduled
+ * for the same IntentSender, it will first be canceled.
+ *
+ * <p>If the time occurs in the past, the alarm will be triggered
+ * immediately. If there is already an alarm for this Intent
+ * scheduled (with the equality of two intents being defined by
+ * {@link Intent#filterEquals}), then it will be removed and replaced by
+ * this one.
+ *
+ * <p>
+ * The alarm is an intent broadcast that goes to a broadcast receiver that
+ * you registered with {@link android.content.Context#registerReceiver}
+ * or through the &lt;receiver&gt; tag in an AndroidManifest.xml file.
+ *
+ * <p>
+ * Alarm intents are delivered with a data extra of type int called
+ * {@link Intent#EXTRA_ALARM_COUNT Intent.EXTRA_ALARM_COUNT} that indicates
+ * how many past alarm events have been accumulated into this intent
+ * broadcast. Recurring alarms that have gone undelivered because the
+ * phone was asleep may have a count greater than one when delivered.
+ *
+ * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC or
+ * RTC_WAKEUP.
+ * @param triggerAtTime Time the alarm should go off, using the
+ * appropriate clock (depending on the alarm type).
+ * @param operation Action to perform when the alarm goes off;
+ * typically comes from {@link PendingIntent#getBroadcast
+ * IntentSender.getBroadcast()}.
+ *
+ * @see android.os.Handler
+ * @see #setRepeating
+ * @see #cancel
+ * @see android.content.Context#sendBroadcast
+ * @see android.content.Context#registerReceiver
+ * @see android.content.Intent#filterEquals
+ * @see #ELAPSED_REALTIME
+ * @see #ELAPSED_REALTIME_WAKEUP
+ * @see #RTC
+ * @see #RTC_WAKEUP
+ */
+ public void set(int type, long triggerAtTime, PendingIntent operation) {
+ try {
+ mService.set(type, triggerAtTime, operation);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ /**
+ * Schedule a repeating alarm. <b>Note: for timing operations (ticks,
+ * timeouts, etc) it is easier and much more efficient to use
+ * {@link android.os.Handler}.</b> If there is already an alarm scheduled
+ * for the same IntentSender, it will first be canceled.
+ *
+ * <p>Like {@link #set}, except you can also
+ * supply a rate at which the alarm will repeat. This alarm continues
+ * repeating until explicitly removed with {@link #cancel}. If the time
+ * occurs in the past, the alarm will be triggered immediately, with an
+ * alarm count depending on how far in the past the trigger time is relative
+ * to the repeat interval.
+ *
+ * <p>If an alarm is delayed (by system sleep, for example, for non
+ * _WAKEUP alarm types), a skipped repeat will be delivered as soon as
+ * possible. After that, future alarms will be delivered according to the
+ * original schedule; they do not drift over time. For example, if you have
+ * set a recurring alarm for the top of every hour but the phone was asleep
+ * from 7:45 until 8:45, an alarm will be sent as soon as the phone awakens,
+ * then the next alarm will be sent at 9:00.
+ *
+ * <p>If your application wants to allow the delivery times to drift in
+ * order to guarantee that at least a certain time interval always elapses
+ * between alarms, then the approach to take is to use one-time alarms,
+ * scheduling the next one yourself when handling each alarm delivery.
+ *
+ * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP}, RTC or
+ * RTC_WAKEUP.
+ * @param triggerAtTime Time the alarm should first go off, using the
+ * appropriate clock (depending on the alarm type).
+ * @param interval Interval between subsequent repeats of the alarm.
+ * @param operation Action to perform when the alarm goes off;
+ * typically comes from {@link PendingIntent#getBroadcast
+ * IntentSender.getBroadcast()}.
+ *
+ * @see android.os.Handler
+ * @see #set
+ * @see #cancel
+ * @see android.content.Context#sendBroadcast
+ * @see android.content.Context#registerReceiver
+ * @see android.content.Intent#filterEquals
+ * @see #ELAPSED_REALTIME
+ * @see #ELAPSED_REALTIME_WAKEUP
+ * @see #RTC
+ * @see #RTC_WAKEUP
+ */
+ public void setRepeating(int type, long triggerAtTime, long interval,
+ PendingIntent operation) {
+ try {
+ mService.setRepeating(type, triggerAtTime, interval, operation);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ /**
+ * Available inexact recurrence intervals recognized by
+ * {@link #setInexactRepeating(int, long, long, PendingIntent)}
+ */
+ public static final long INTERVAL_FIFTEEN_MINUTES = 15 * 60 * 1000;
+ public static final long INTERVAL_HALF_HOUR = 2*INTERVAL_FIFTEEN_MINUTES;
+ public static final long INTERVAL_HOUR = 2*INTERVAL_HALF_HOUR;
+ public static final long INTERVAL_HALF_DAY = 12*INTERVAL_HOUR;
+ public static final long INTERVAL_DAY = 2*INTERVAL_HALF_DAY;
+
+ /**
+ * Schedule a repeating alarm that has inexact trigger time requirements;
+ * for example, an alarm that repeats every hour, but not necessarily at
+ * the top of every hour. These alarms are more power-efficient than
+ * the strict recurrences supplied by {@link #setRepeating}, since the
+ * system can adjust alarms' phase to cause them to fire simultaneously,
+ * avoiding waking the device from sleep more than necessary.
+ *
+ * <p>Your alarm's first trigger will not be before the requested time,
+ * but it might not occur for almost a full interval after that time. In
+ * addition, while the overall period of the repeating alarm will be as
+ * requested, the time between any two successive firings of the alarm
+ * may vary. If your application demands very low jitter, use
+ * {@link #setRepeating} instead.
+ *
+ * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP}, RTC or
+ * RTC_WAKEUP.
+ * @param triggerAtTime Time the alarm should first go off, using the
+ * appropriate clock (depending on the alarm type). This
+ * is inexact: the alarm will not fire before this time,
+ * but there may be a delay of almost an entire alarm
+ * interval before the first invocation of the alarm.
+ * @param interval Interval between subsequent repeats of the alarm. If
+ * this is one of INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR,
+ * INTERVAL_HOUR, INTERVAL_HALF_DAY, or INTERVAL_DAY then the
+ * alarm will be phase-aligned with other alarms to reduce
+ * the number of wakeups. Otherwise, the alarm will be set
+ * as though the application had called {@link #setRepeating}.
+ * @param operation Action to perform when the alarm goes off;
+ * typically comes from {@link PendingIntent#getBroadcast
+ * IntentSender.getBroadcast()}.
+ *
+ * @see android.os.Handler
+ * @see #set
+ * @see #cancel
+ * @see android.content.Context#sendBroadcast
+ * @see android.content.Context#registerReceiver
+ * @see android.content.Intent#filterEquals
+ * @see #ELAPSED_REALTIME
+ * @see #ELAPSED_REALTIME_WAKEUP
+ * @see #RTC
+ * @see #RTC_WAKEUP
+ * @see #INTERVAL_FIFTEEN_MINUTES
+ * @see #INTERVAL_HALF_HOUR
+ * @see #INTERVAL_HOUR
+ * @see #INTERVAL_HALF_DAY
+ * @see #INTERVAL_DAY
+ */
+ public void setInexactRepeating(int type, long triggerAtTime, long interval,
+ PendingIntent operation) {
+ try {
+ mService.setInexactRepeating(type, triggerAtTime, interval, operation);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ /**
+ * Remove any alarms with a matching {@link Intent}.
+ * Any alarm, of any type, whose Intent matches this one (as defined by
+ * {@link Intent#filterEquals}), will be canceled.
+ *
+ * @param operation IntentSender which matches a previously added
+ * IntentSender.
+ *
+ * @see #set
+ */
+ public void cancel(PendingIntent operation) {
+ try {
+ mService.remove(operation);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ public void setTimeZone(String timeZone) {
+ try {
+ mService.setTimeZone(timeZone);
+ } catch (RemoteException ex) {
+ }
+ }
+}
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
new file mode 100644
index 0000000..f2b89c3
--- /dev/null
+++ b/core/java/android/app/AlertDialog.java
@@ -0,0 +1,795 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import com.android.internal.app.AlertController;
+
+/**
+ * A subclass of Dialog that can display one, two or three buttons. If you only want to
+ * display a String in this dialog box, use the setMessage() method. If you
+ * want to display a more complex view, look up the FrameLayout called "body"
+ * and add your view to it:
+ *
+ * <pre>
+ * FrameLayout fl = (FrameLayout) findViewById(R.id.body);
+ * fl.add(myView, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
+ * </pre>
+ *
+ * <p>The AlertDialog class takes care of automatically setting
+ * {@link WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
+ * WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} for you based on whether
+ * any views in the dialog return true from {@link View#onCheckIsTextEditor()
+ * View.onCheckIsTextEditor()}. Generally you want this set for a Dialog
+ * without text editors, so that it will be placed on top of the current
+ * input method UI. You can modify this behavior by forcing the flag to your
+ * desired mode after calling {@link #onCreate}.
+ */
+public class AlertDialog extends Dialog implements DialogInterface {
+ private AlertController mAlert;
+
+ protected AlertDialog(Context context) {
+ this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+ }
+
+ protected AlertDialog(Context context, int theme) {
+ super(context, theme);
+ mAlert = new AlertController(context, this, getWindow());
+ }
+
+ protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
+ super(context, com.android.internal.R.style.Theme_Dialog_Alert);
+ setCancelable(cancelable);
+ setOnCancelListener(cancelListener);
+ mAlert = new AlertController(context, this, getWindow());
+ }
+
+ /**
+ * Gets one of the buttons used in the dialog.
+ * <p>
+ * If a button does not exist in the dialog, null will be returned.
+ *
+ * @param whichButton The identifier of the button that should be returned.
+ * For example, this can be
+ * {@link DialogInterface#BUTTON_POSITIVE}.
+ * @return The button from the dialog, or null if a button does not exist.
+ */
+ public Button getButton(int whichButton) {
+ return mAlert.getButton(whichButton);
+ }
+
+ /**
+ * Gets the list view used in the dialog.
+ *
+ * @return The {@link ListView} from the dialog.
+ */
+ public ListView getListView() {
+ return mAlert.getListView();
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ super.setTitle(title);
+ mAlert.setTitle(title);
+ }
+
+ /**
+ * @see Builder#setCustomTitle(View)
+ */
+ public void setCustomTitle(View customTitleView) {
+ mAlert.setCustomTitle(customTitleView);
+ }
+
+ public void setMessage(CharSequence message) {
+ mAlert.setMessage(message);
+ }
+
+ /**
+ * Set the view to display in that dialog.
+ */
+ public void setView(View view) {
+ mAlert.setView(view);
+ }
+
+ /**
+ * Set the view to display in that dialog, specifying the spacing to appear around that
+ * view.
+ *
+ * @param view The view to show in the content area of the dialog
+ * @param viewSpacingLeft Extra space to appear to the left of {@code view}
+ * @param viewSpacingTop Extra space to appear above {@code view}
+ * @param viewSpacingRight Extra space to appear to the right of {@code view}
+ * @param viewSpacingBottom Extra space to appear below {@code view}
+ */
+ public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
+ int viewSpacingBottom) {
+ mAlert.setView(view, viewSpacingLeft, viewSpacingTop, viewSpacingRight, viewSpacingBottom);
+ }
+
+ /**
+ * Set a message to be sent when a button is pressed.
+ *
+ * @param whichButton Which button to set the message for, can be one of
+ * {@link DialogInterface#BUTTON_POSITIVE},
+ * {@link DialogInterface#BUTTON_NEGATIVE}, or
+ * {@link DialogInterface#BUTTON_NEUTRAL}
+ * @param text The text to display in positive button.
+ * @param msg The {@link Message} to be sent when clicked.
+ */
+ public void setButton(int whichButton, CharSequence text, Message msg) {
+ mAlert.setButton(whichButton, text, null, msg);
+ }
+
+ /**
+ * Set a listener to be invoked when the positive button of the dialog is pressed.
+ *
+ * @param whichButton Which button to set the listener on, can be one of
+ * {@link DialogInterface#BUTTON_POSITIVE},
+ * {@link DialogInterface#BUTTON_NEGATIVE}, or
+ * {@link DialogInterface#BUTTON_NEUTRAL}
+ * @param text The text to display in positive button.
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ */
+ public void setButton(int whichButton, CharSequence text, OnClickListener listener) {
+ mAlert.setButton(whichButton, text, listener, null);
+ }
+
+ /**
+ * @deprecated Use {@link #setButton(int, CharSequence, Message)} with
+ * {@link DialogInterface#BUTTON_POSITIVE}.
+ */
+ @Deprecated
+ public void setButton(CharSequence text, Message msg) {
+ setButton(BUTTON_POSITIVE, text, msg);
+ }
+
+ /**
+ * @deprecated Use {@link #setButton(int, CharSequence, Message)} with
+ * {@link DialogInterface#BUTTON_NEGATIVE}.
+ */
+ @Deprecated
+ public void setButton2(CharSequence text, Message msg) {
+ setButton(BUTTON_NEGATIVE, text, msg);
+ }
+
+ /**
+ * @deprecated Use {@link #setButton(int, CharSequence, Message)} with
+ * {@link DialogInterface#BUTTON_NEUTRAL}.
+ */
+ @Deprecated
+ public void setButton3(CharSequence text, Message msg) {
+ setButton(BUTTON_NEUTRAL, text, msg);
+ }
+
+ /**
+ * Set a listener to be invoked when button 1 of the dialog is pressed.
+ *
+ * @param text The text to display in button 1.
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ * @deprecated Use
+ * {@link #setButton(int, CharSequence, android.content.DialogInterface.OnClickListener)}
+ * with {@link DialogInterface#BUTTON_POSITIVE}
+ */
+ @Deprecated
+ public void setButton(CharSequence text, final OnClickListener listener) {
+ setButton(BUTTON_POSITIVE, text, listener);
+ }
+
+ /**
+ * Set a listener to be invoked when button 2 of the dialog is pressed.
+ * @param text The text to display in button 2.
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ * @deprecated Use
+ * {@link #setButton(int, CharSequence, android.content.DialogInterface.OnClickListener)}
+ * with {@link DialogInterface#BUTTON_NEGATIVE}
+ */
+ @Deprecated
+ public void setButton2(CharSequence text, final OnClickListener listener) {
+ setButton(BUTTON_NEGATIVE, text, listener);
+ }
+
+ /**
+ * Set a listener to be invoked when button 3 of the dialog is pressed.
+ * @param text The text to display in button 3.
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ * @deprecated Use
+ * {@link #setButton(int, CharSequence, android.content.DialogInterface.OnClickListener)}
+ * with {@link DialogInterface#BUTTON_POSITIVE}
+ */
+ @Deprecated
+ public void setButton3(CharSequence text, final OnClickListener listener) {
+ setButton(BUTTON_NEUTRAL, text, listener);
+ }
+
+ /**
+ * Set resId to 0 if you don't want an icon.
+ * @param resId the resourceId of the drawable to use as the icon or 0
+ * if you don't want an icon.
+ */
+ public void setIcon(int resId) {
+ mAlert.setIcon(resId);
+ }
+
+ public void setIcon(Drawable icon) {
+ mAlert.setIcon(icon);
+ }
+
+ public void setInverseBackgroundForced(boolean forceInverseBackground) {
+ mAlert.setInverseBackgroundForced(forceInverseBackground);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAlert.installContent();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (mAlert.onKeyDown(keyCode, event)) return true;
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (mAlert.onKeyUp(keyCode, event)) return true;
+ return super.onKeyUp(keyCode, event);
+ }
+
+ public static class Builder {
+ private final AlertController.AlertParams P;
+
+ /**
+ * Constructor using a context for this builder and the {@link AlertDialog} it creates.
+ */
+ public Builder(Context context) {
+ P = new AlertController.AlertParams(context);
+ }
+
+ /**
+ * Set the title using the given resource id.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setTitle(int titleId) {
+ P.mTitle = P.mContext.getText(titleId);
+ return this;
+ }
+
+ /**
+ * Set the title displayed in the {@link Dialog}.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setTitle(CharSequence title) {
+ P.mTitle = title;
+ return this;
+ }
+
+ /**
+ * Set the title using the custom view {@code customTitleView}. The
+ * methods {@link #setTitle(int)} and {@link #setIcon(int)} should be
+ * sufficient for most titles, but this is provided if the title needs
+ * more customization. Using this will replace the title and icon set
+ * via the other methods.
+ *
+ * @param customTitleView The custom view to use as the title.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setCustomTitle(View customTitleView) {
+ P.mCustomTitleView = customTitleView;
+ return this;
+ }
+
+ /**
+ * Set the message to display using the given resource id.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setMessage(int messageId) {
+ P.mMessage = P.mContext.getText(messageId);
+ return this;
+ }
+
+ /**
+ * Set the message to display.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setMessage(CharSequence message) {
+ P.mMessage = message;
+ return this;
+ }
+
+ /**
+ * Set the resource id of the {@link Drawable} to be used in the title.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setIcon(int iconId) {
+ P.mIconId = iconId;
+ return this;
+ }
+
+ /**
+ * Set the {@link Drawable} to be used in the title.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setIcon(Drawable icon) {
+ P.mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Set a listener to be invoked when the positive button of the dialog is pressed.
+ * @param textId The resource id of the text to display in the positive button
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setPositiveButton(int textId, final OnClickListener listener) {
+ P.mPositiveButtonText = P.mContext.getText(textId);
+ P.mPositiveButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Set a listener to be invoked when the positive button of the dialog is pressed.
+ * @param text The text to display in the positive button
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
+ P.mPositiveButtonText = text;
+ P.mPositiveButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Set a listener to be invoked when the negative button of the dialog is pressed.
+ * @param textId The resource id of the text to display in the negative button
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setNegativeButton(int textId, final OnClickListener listener) {
+ P.mNegativeButtonText = P.mContext.getText(textId);
+ P.mNegativeButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Set a listener to be invoked when the negative button of the dialog is pressed.
+ * @param text The text to display in the negative button
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setNegativeButton(CharSequence text, final OnClickListener listener) {
+ P.mNegativeButtonText = text;
+ P.mNegativeButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Set a listener to be invoked when the neutral button of the dialog is pressed.
+ * @param textId The resource id of the text to display in the neutral button
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setNeutralButton(int textId, final OnClickListener listener) {
+ P.mNeutralButtonText = P.mContext.getText(textId);
+ P.mNeutralButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Set a listener to be invoked when the neutral button of the dialog is pressed.
+ * @param text The text to display in the neutral button
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setNeutralButton(CharSequence text, final OnClickListener listener) {
+ P.mNeutralButtonText = text;
+ P.mNeutralButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Sets whether the dialog is cancelable or not default is true.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setCancelable(boolean cancelable) {
+ P.mCancelable = cancelable;
+ return this;
+ }
+
+ /**
+ * Sets the callback that will be called if the dialog is canceled.
+ * @see #setCancelable(boolean)
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setOnCancelListener(OnCancelListener onCancelListener) {
+ P.mOnCancelListener = onCancelListener;
+ return this;
+ }
+
+ /**
+ * Sets the callback that will be called if a key is dispatched to the dialog.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setOnKeyListener(OnKeyListener onKeyListener) {
+ P.mOnKeyListener = onKeyListener;
+ return this;
+ }
+
+ /**
+ * Set a list of items to be displayed in the dialog as the content, you will be notified of the
+ * selected item via the supplied listener. This should be an array type i.e. R.array.foo
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setItems(int itemsId, final OnClickListener listener) {
+ P.mItems = P.mContext.getResources().getTextArray(itemsId);
+ P.mOnClickListener = listener;
+ return this;
+ }
+
+ /**
+ * Set a list of items to be displayed in the dialog as the content, you will be notified of the
+ * selected item via the supplied listener.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setItems(CharSequence[] items, final OnClickListener listener) {
+ P.mItems = items;
+ P.mOnClickListener = listener;
+ return this;
+ }
+
+ /**
+ * Set a list of items, which are supplied by the given {@link ListAdapter}, to be
+ * displayed in the dialog as the content, you will be notified of the
+ * selected item via the supplied listener.
+ *
+ * @param adapter The {@link ListAdapter} to supply the list of items
+ * @param listener The listener that will be called when an item is clicked.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setAdapter(final ListAdapter adapter, final OnClickListener listener) {
+ P.mAdapter = adapter;
+ P.mOnClickListener = listener;
+ return this;
+ }
+
+ /**
+ * Set a list of items, which are supplied by the given {@link Cursor}, to be
+ * displayed in the dialog as the content, you will be notified of the
+ * selected item via the supplied listener.
+ *
+ * @param cursor The {@link Cursor} to supply the list of items
+ * @param listener The listener that will be called when an item is clicked.
+ * @param labelColumn The column name on the cursor containing the string to display
+ * in the label.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setCursor(final Cursor cursor, final OnClickListener listener,
+ String labelColumn) {
+ P.mCursor = cursor;
+ P.mLabelColumn = labelColumn;
+ P.mOnClickListener = listener;
+ return this;
+ }
+
+ /**
+ * Set a list of items to be displayed in the dialog as the content,
+ * you will be notified of the selected item via the supplied listener.
+ * This should be an array type, e.g. R.array.foo. The list will have
+ * a check mark displayed to the right of the text for each checked
+ * item. Clicking on an item in the list will not dismiss the dialog.
+ * Clicking on a button will dismiss the dialog.
+ *
+ * @param itemsId the resource id of an array i.e. R.array.foo
+ * @param checkedItems specifies which items are checked. It should be null in which case no
+ * items are checked. If non null it must be exactly the same length as the array of
+ * items.
+ * @param listener notified when an item on the list is clicked. The dialog will not be
+ * dismissed when an item is clicked. It will only be dismissed if clicked on a
+ * button, if no buttons are supplied it's up to the user to dismiss the dialog.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setMultiChoiceItems(int itemsId, boolean[] checkedItems,
+ final OnMultiChoiceClickListener listener) {
+ P.mItems = P.mContext.getResources().getTextArray(itemsId);
+ P.mOnCheckboxClickListener = listener;
+ P.mCheckedItems = checkedItems;
+ P.mIsMultiChoice = true;
+ return this;
+ }
+
+ /**
+ * Set a list of items to be displayed in the dialog as the content,
+ * you will be notified of the selected item via the supplied listener.
+ * The list will have a check mark displayed to the right of the text
+ * for each checked item. Clicking on an item in the list will not
+ * dismiss the dialog. Clicking on a button will dismiss the dialog.
+ *
+ * @param items the text of the items to be displayed in the list.
+ * @param checkedItems specifies which items are checked. It should be null in which case no
+ * items are checked. If non null it must be exactly the same length as the array of
+ * items.
+ * @param listener notified when an item on the list is clicked. The dialog will not be
+ * dismissed when an item is clicked. It will only be dismissed if clicked on a
+ * button, if no buttons are supplied it's up to the user to dismiss the dialog.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems,
+ final OnMultiChoiceClickListener listener) {
+ P.mItems = items;
+ P.mOnCheckboxClickListener = listener;
+ P.mCheckedItems = checkedItems;
+ P.mIsMultiChoice = true;
+ return this;
+ }
+
+ /**
+ * Set a list of items to be displayed in the dialog as the content,
+ * you will be notified of the selected item via the supplied listener.
+ * The list will have a check mark displayed to the right of the text
+ * for each checked item. Clicking on an item in the list will not
+ * dismiss the dialog. Clicking on a button will dismiss the dialog.
+ *
+ * @param cursor the cursor used to provide the items.
+ * @param isCheckedColumn specifies the column name on the cursor to use to determine
+ * whether a checkbox is checked or not. It must return an integer value where 1
+ * means checked and 0 means unchecked.
+ * @param labelColumn The column name on the cursor containing the string to display in the
+ * label.
+ * @param listener notified when an item on the list is clicked. The dialog will not be
+ * dismissed when an item is clicked. It will only be dismissed if clicked on a
+ * button, if no buttons are supplied it's up to the user to dismiss the dialog.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setMultiChoiceItems(Cursor cursor, String isCheckedColumn, String labelColumn,
+ final OnMultiChoiceClickListener listener) {
+ P.mCursor = cursor;
+ P.mOnCheckboxClickListener = listener;
+ P.mIsCheckedColumn = isCheckedColumn;
+ P.mLabelColumn = labelColumn;
+ P.mIsMultiChoice = true;
+ return this;
+ }
+
+ /**
+ * Set a list of items to be displayed in the dialog as the content, you will be notified of
+ * the selected item via the supplied listener. This should be an array type i.e.
+ * R.array.foo The list will have a check mark displayed to the right of the text for the
+ * checked item. Clicking on an item in the list will not dismiss the dialog. Clicking on a
+ * button will dismiss the dialog.
+ *
+ * @param itemsId the resource id of an array i.e. R.array.foo
+ * @param checkedItem specifies which item is checked. If -1 no items are checked.
+ * @param listener notified when an item on the list is clicked. The dialog will not be
+ * dismissed when an item is clicked. It will only be dismissed if clicked on a
+ * button, if no buttons are supplied it's up to the user to dismiss the dialog.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setSingleChoiceItems(int itemsId, int checkedItem,
+ final OnClickListener listener) {
+ P.mItems = P.mContext.getResources().getTextArray(itemsId);
+ P.mOnClickListener = listener;
+ P.mCheckedItem = checkedItem;
+ P.mIsSingleChoice = true;
+ return this;
+ }
+
+ /**
+ * Set a list of items to be displayed in the dialog as the content, you will be notified of
+ * the selected item via the supplied listener. The list will have a check mark displayed to
+ * the right of the text for the checked item. Clicking on an item in the list will not
+ * dismiss the dialog. Clicking on a button will dismiss the dialog.
+ *
+ * @param cursor the cursor to retrieve the items from.
+ * @param checkedItem specifies which item is checked. If -1 no items are checked.
+ * @param labelColumn The column name on the cursor containing the string to display in the
+ * label.
+ * @param listener notified when an item on the list is clicked. The dialog will not be
+ * dismissed when an item is clicked. It will only be dismissed if clicked on a
+ * button, if no buttons are supplied it's up to the user to dismiss the dialog.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setSingleChoiceItems(Cursor cursor, int checkedItem, String labelColumn,
+ final OnClickListener listener) {
+ P.mCursor = cursor;
+ P.mOnClickListener = listener;
+ P.mCheckedItem = checkedItem;
+ P.mLabelColumn = labelColumn;
+ P.mIsSingleChoice = true;
+ return this;
+ }
+
+ /**
+ * Set a list of items to be displayed in the dialog as the content, you will be notified of
+ * the selected item via the supplied listener. The list will have a check mark displayed to
+ * the right of the text for the checked item. Clicking on an item in the list will not
+ * dismiss the dialog. Clicking on a button will dismiss the dialog.
+ *
+ * @param items the items to be displayed.
+ * @param checkedItem specifies which item is checked. If -1 no items are checked.
+ * @param listener notified when an item on the list is clicked. The dialog will not be
+ * dismissed when an item is clicked. It will only be dismissed if clicked on a
+ * button, if no buttons are supplied it's up to the user to dismiss the dialog.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setSingleChoiceItems(CharSequence[] items, int checkedItem, final OnClickListener listener) {
+ P.mItems = items;
+ P.mOnClickListener = listener;
+ P.mCheckedItem = checkedItem;
+ P.mIsSingleChoice = true;
+ return this;
+ }
+
+ /**
+ * Set a list of items to be displayed in the dialog as the content, you will be notified of
+ * the selected item via the supplied listener. The list will have a check mark displayed to
+ * the right of the text for the checked item. Clicking on an item in the list will not
+ * dismiss the dialog. Clicking on a button will dismiss the dialog.
+ *
+ * @param adapter The {@link ListAdapter} to supply the list of items
+ * @param checkedItem specifies which item is checked. If -1 no items are checked.
+ * @param listener notified when an item on the list is clicked. The dialog will not be
+ * dismissed when an item is clicked. It will only be dismissed if clicked on a
+ * button, if no buttons are supplied it's up to the user to dismiss the dialog.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setSingleChoiceItems(ListAdapter adapter, int checkedItem, final OnClickListener listener) {
+ P.mAdapter = adapter;
+ P.mOnClickListener = listener;
+ P.mCheckedItem = checkedItem;
+ P.mIsSingleChoice = true;
+ return this;
+ }
+
+ /**
+ * Sets a listener to be invoked when an item in the list is selected.
+ *
+ * @param listener The listener to be invoked.
+ * @see AdapterView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setOnItemSelectedListener(final AdapterView.OnItemSelectedListener listener) {
+ P.mOnItemSelectedListener = listener;
+ return this;
+ }
+
+ /**
+ * Set a custom view to be the contents of the Dialog. If the supplied view is an instance
+ * of a {@link ListView} the light background will be used.
+ *
+ * @param view The view to use as the contents of the Dialog.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setView(View view) {
+ P.mView = view;
+ P.mViewSpacingSpecified = false;
+ return this;
+ }
+
+ /**
+ * Set a custom view to be the contents of the Dialog, specifying the
+ * spacing to appear around that view. If the supplied view is an
+ * instance of a {@link ListView} the light background will be used.
+ *
+ * @param view The view to use as the contents of the Dialog.
+ * @param viewSpacingLeft Spacing between the left edge of the view and
+ * the dialog frame
+ * @param viewSpacingTop Spacing between the top edge of the view and
+ * the dialog frame
+ * @param viewSpacingRight Spacing between the right edge of the view
+ * and the dialog frame
+ * @param viewSpacingBottom Spacing between the bottom edge of the view
+ * and the dialog frame
+ * @return This Builder object to allow for chaining of calls to set
+ * methods
+ *
+ * @hide pending API review
+ */
+ public Builder setView(View view, int viewSpacingLeft, int viewSpacingTop,
+ int viewSpacingRight, int viewSpacingBottom) {
+ P.mView = view;
+ P.mViewSpacingSpecified = true;
+ P.mViewSpacingLeft = viewSpacingLeft;
+ P.mViewSpacingTop = viewSpacingTop;
+ P.mViewSpacingRight = viewSpacingRight;
+ P.mViewSpacingBottom = viewSpacingBottom;
+ return this;
+ }
+
+ /**
+ * Sets the Dialog to use the inverse background, regardless of what the
+ * contents is.
+ *
+ * @param useInverseBackground Whether to use the inverse background
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setInverseBackgroundForced(boolean useInverseBackground) {
+ P.mForceInverseBackground = useInverseBackground;
+ return this;
+ }
+
+ /**
+ * Creates a {@link AlertDialog} with the arguments supplied to this builder. It does not
+ * {@link Dialog#show()} the dialog. This allows the user to do any extra processing
+ * before displaying the dialog. Use {@link #show()} if you don't have any other processing
+ * to do and want this to be created and displayed.
+ */
+ public AlertDialog create() {
+ final AlertDialog dialog = new AlertDialog(P.mContext);
+ P.apply(dialog.mAlert);
+ dialog.setCancelable(P.mCancelable);
+ dialog.setOnCancelListener(P.mOnCancelListener);
+ if (P.mOnKeyListener != null) {
+ dialog.setOnKeyListener(P.mOnKeyListener);
+ }
+ return dialog;
+ }
+
+ /**
+ * Creates a {@link AlertDialog} with the arguments supplied to this builder and
+ * {@link Dialog#show()}'s the dialog.
+ */
+ public AlertDialog show() {
+ AlertDialog dialog = create();
+ dialog.show();
+ return dialog;
+ }
+ }
+
+}
diff --git a/core/java/android/app/AliasActivity.java b/core/java/android/app/AliasActivity.java
new file mode 100644
index 0000000..4f91e02
--- /dev/null
+++ b/core/java/android/app/AliasActivity.java
@@ -0,0 +1,123 @@
+/*
+ * 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.app;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+
+import java.io.IOException;
+
+/**
+ * Stub activity that launches another activity (and then finishes itself)
+ * based on information in its component's manifest meta-data. This is a
+ * simple way to implement an alias-like mechanism.
+ *
+ * To use this activity, you should include in the manifest for the associated
+ * component an entry named "android.app.alias". It is a reference to an XML
+ * resource describing an intent that launches the real application.
+ */
+public class AliasActivity extends Activity {
+ /**
+ * This is the name under which you should store in your component the
+ * meta-data information about the alias. It is a reference to an XML
+ * resource describing an intent that launches the real application.
+ * {@hide}
+ */
+ public final String ALIAS_META_DATA = "android.app.alias";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ XmlResourceParser parser = null;
+ try {
+ ActivityInfo ai = getPackageManager().getActivityInfo(
+ getComponentName(), PackageManager.GET_META_DATA);
+ parser = ai.loadXmlMetaData(getPackageManager(),
+ ALIAS_META_DATA);
+ if (parser == null) {
+ throw new RuntimeException("Alias requires a meta-data field "
+ + ALIAS_META_DATA);
+ }
+
+ Intent intent = parseAlias(parser);
+ if (intent == null) {
+ throw new RuntimeException(
+ "No <intent> tag found in alias description");
+ }
+
+ startActivity(intent);
+ finish();
+
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("Error parsing alias", e);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException("Error parsing alias", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Error parsing alias", e);
+ } finally {
+ if (parser != null) parser.close();
+ }
+ }
+
+ private Intent parseAlias(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ Intent intent = null;
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ String nodeName = parser.getName();
+ if (!"alias".equals(nodeName)) {
+ throw new RuntimeException(
+ "Alias meta-data must start with <alias> tag; found"
+ + nodeName + " at " + parser.getPositionDescription());
+ }
+
+ int outerDepth = parser.getDepth();
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ nodeName = parser.getName();
+ if ("intent".equals(nodeName)) {
+ Intent gotIntent = Intent.parseIntent(getResources(), parser, attrs);
+ if (intent == null) intent = gotIntent;
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ return intent;
+ }
+
+}
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
new file mode 100644
index 0000000..45ce860
--- /dev/null
+++ b/core/java/android/app/Application.java
@@ -0,0 +1,74 @@
+/*
+ * 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.app;
+
+import android.content.ComponentCallbacks;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Configuration;
+
+/**
+ * Base class for those who need to maintain global application state. You can
+ * provide your own implementation by specifying its name in your
+ * AndroidManifest.xml's &lt;application&gt; tag, which will cause that class
+ * to be instantiated for you when the process for your application/package is
+ * created.
+ */
+public class Application extends ContextWrapper implements ComponentCallbacks {
+
+ public Application() {
+ super(null);
+ }
+
+ /**
+ * Called when the application is starting, before any other application
+ * objects have been created. Implementations should be as quick as
+ * possible (for example using lazy initialization of state) since the time
+ * spent in this function directly impacts the performance of starting the
+ * first activity, service, or receiver in a process.
+ * If you override this method, be sure to call super.onCreate().
+ */
+ public void onCreate() {
+ }
+
+ /**
+ * Called when the application is stopping. There are no more application
+ * objects running and the process will exit. <em>Note: never depend on
+ * this method being called; in many cases an unneeded application process
+ * will simply be killed by the kernel without executing any application
+ * code.</em>
+ * If you override this method, be sure to call super.onTerminate().
+ */
+ public void onTerminate() {
+ }
+
+ public void onConfigurationChanged(Configuration newConfig) {
+ }
+
+ public void onLowMemory() {
+ }
+
+ // ------------------ Internal API ------------------
+
+ /**
+ * @hide
+ */
+ /* package */ final void attach(Context context) {
+ attachBaseContext(context);
+ }
+
+}
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
new file mode 100644
index 0000000..3b5ad86
--- /dev/null
+++ b/core/java/android/app/ApplicationContext.java
@@ -0,0 +1,2765 @@
+/*
+ * 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.app;
+
+import com.google.android.collect.Maps;
+import com.android.internal.util.XmlUtils;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ReceiverCallNotAllowedException;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IPackageInstallObserver;
+import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.hardware.SensorManager;
+import android.location.ILocationManager;
+import android.location.LocationManager;
+import android.media.AudioManager;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.net.Uri;
+import android.net.wifi.IWifiManager;
+import android.net.wifi.WifiManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.Vibrator;
+import android.os.FileUtils.FileStatus;
+import android.telephony.TelephonyManager;
+import android.text.ClipboardManager;
+import android.util.AndroidRuntimeException;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.WindowManagerImpl;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.policy.PolicyManager;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+class ReceiverRestrictedContext extends ContextWrapper {
+ ReceiverRestrictedContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ return registerReceiver(receiver, filter, null, null);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ throw new ReceiverCallNotAllowedException(
+ "IntentReceiver components are not allowed to register to receive intents");
+ //ex.fillInStackTrace();
+ //Log.e("IntentReceiver", ex.getMessage(), ex);
+ //return mContext.registerReceiver(receiver, filter, broadcastPermission,
+ // scheduler);
+ }
+
+ @Override
+ public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+ throw new ReceiverCallNotAllowedException(
+ "IntentReceiver components are not allowed to bind to services");
+ //ex.fillInStackTrace();
+ //Log.e("IntentReceiver", ex.getMessage(), ex);
+ //return mContext.bindService(service, interfaceName, conn, flags);
+ }
+}
+
+/**
+ * Common implementation of Context API, which Activity and other application
+ * classes inherit.
+ */
+class ApplicationContext extends Context {
+ private final static String TAG = "ApplicationContext";
+ private final static boolean DEBUG_ICONS = false;
+
+ private static final Object sSync = new Object();
+ private static AlarmManager sAlarmManager;
+ private static PowerManager sPowerManager;
+ private static ConnectivityManager sConnectivityManager;
+ private static WifiManager sWifiManager;
+ private static LocationManager sLocationManager;
+ private static boolean sIsBluetoothDeviceCached = false;
+ private static BluetoothDevice sBluetoothDevice;
+ private static IWallpaperService sWallpaperService;
+ private static final HashMap<File, SharedPreferencesImpl> sSharedPrefs =
+ new HashMap<File, SharedPreferencesImpl>();
+
+ private AudioManager mAudioManager;
+ /*package*/ ActivityThread.PackageInfo mPackageInfo;
+ private Resources mResources;
+ /*package*/ ActivityThread mMainThread;
+ private Context mOuterContext;
+ private IBinder mActivityToken = null;
+ private ApplicationContentResolver mContentResolver;
+ private int mThemeResource = 0;
+ private Resources.Theme mTheme = null;
+ private PackageManager mPackageManager;
+ private NotificationManager mNotificationManager = null;
+ private ActivityManager mActivityManager = null;
+ private Context mReceiverRestrictedContext = null;
+ private SearchManager mSearchManager = null;
+ private SensorManager mSensorManager = null;
+ private Vibrator mVibrator = null;
+ private LayoutInflater mLayoutInflater = null;
+ private StatusBarManager mStatusBarManager = null;
+ private TelephonyManager mTelephonyManager = null;
+ private ClipboardManager mClipboardManager = null;
+
+ private final Object mSync = new Object();
+
+ private File mDatabasesDir;
+ private File mPreferencesDir;
+ private File mFilesDir;
+
+
+ private File mCacheDir;
+
+ private Drawable mWallpaper;
+ private IWallpaperServiceCallback mWallpaperCallback = null;
+
+ private static long sInstanceCount = 0;
+
+ private static final String[] EMPTY_FILE_LIST = {};
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ --sInstanceCount;
+ }
+
+ public static long getInstanceCount() {
+ return sInstanceCount;
+ }
+
+ @Override
+ public AssetManager getAssets() {
+ return mResources.getAssets();
+ }
+
+ @Override
+ public Resources getResources() {
+ return mResources;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ if (mPackageManager != null) {
+ return mPackageManager;
+ }
+
+ IPackageManager pm = ActivityThread.getPackageManager();
+ if (pm != null) {
+ // Doesn't matter if we make more than one instance.
+ return (mPackageManager = new ApplicationPackageManager(this, pm));
+ }
+
+ return null;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ @Override
+ public Looper getMainLooper() {
+ return mMainThread.getLooper();
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return mMainThread.getApplication();
+ }
+
+ @Override
+ public void setTheme(int resid) {
+ mThemeResource = resid;
+ }
+
+ @Override
+ public Resources.Theme getTheme() {
+ if (mTheme == null) {
+ if (mThemeResource == 0) {
+ mThemeResource = com.android.internal.R.style.Theme;
+ }
+ mTheme = mResources.newTheme();
+ mTheme.applyStyle(mThemeResource, true);
+ }
+ return mTheme;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return mPackageInfo != null ?
+ mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader();
+ }
+
+ @Override
+ public String getPackageName() {
+ if (mPackageInfo != null) {
+ return mPackageInfo.getPackageName();
+ }
+ throw new RuntimeException("Not supported in system context");
+ }
+
+ @Override
+ public String getPackageResourcePath() {
+ if (mPackageInfo != null) {
+ return mPackageInfo.getResDir();
+ }
+ throw new RuntimeException("Not supported in system context");
+ }
+
+ @Override
+ public String getPackageCodePath() {
+ if (mPackageInfo != null) {
+ return mPackageInfo.getAppDir();
+ }
+ throw new RuntimeException("Not supported in system context");
+ }
+
+ private static File makeBackupFile(File prefsFile) {
+ return new File(prefsFile.getPath() + ".bak");
+ }
+
+ @Override
+ public SharedPreferences getSharedPreferences(String name, int mode) {
+ SharedPreferencesImpl sp;
+ File f = makeFilename(getPreferencesDir(), name + ".xml");
+ synchronized (sSharedPrefs) {
+ sp = sSharedPrefs.get(f);
+ if (sp != null && !sp.hasFileChanged()) {
+ //Log.i(TAG, "Returning existing prefs " + name + ": " + sp);
+ return sp;
+ }
+ }
+
+ FileInputStream str = null;
+ File backup = makeBackupFile(f);
+ if (backup.exists()) {
+ f.delete();
+ backup.renameTo(f);
+ }
+
+ // Debugging
+ if (f.exists() && !f.canRead()) {
+ Log.w(TAG, "Attempt to read preferences file " + f + " without permission");
+ }
+
+ Map map = null;
+ if (f.exists() && f.canRead()) {
+ try {
+ str = new FileInputStream(f);
+ map = XmlUtils.readMapXml(str);
+ str.close();
+ } catch (org.xmlpull.v1.XmlPullParserException e) {
+ Log.w(TAG, "getSharedPreferences", e);
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "getSharedPreferences", e);
+ } catch (IOException e) {
+ Log.w(TAG, "getSharedPreferences", e);
+ }
+ }
+
+ synchronized (sSharedPrefs) {
+ if (sp != null) {
+ //Log.i(TAG, "Updating existing prefs " + name + " " + sp + ": " + map);
+ sp.replace(map);
+ } else {
+ sp = sSharedPrefs.get(f);
+ if (sp == null) {
+ sp = new SharedPreferencesImpl(f, mode, map);
+ sSharedPrefs.put(f, sp);
+ }
+ }
+ return sp;
+ }
+ }
+
+ private File getPreferencesDir() {
+ synchronized (mSync) {
+ if (mPreferencesDir == null) {
+ mPreferencesDir = new File(getDataDirFile(), "shared_prefs");
+ }
+ return mPreferencesDir;
+ }
+ }
+
+ @Override
+ public FileInputStream openFileInput(String name)
+ throws FileNotFoundException {
+ File f = makeFilename(getFilesDir(), name);
+ return new FileInputStream(f);
+ }
+
+ @Override
+ public FileOutputStream openFileOutput(String name, int mode)
+ throws FileNotFoundException {
+ final boolean append = (mode&MODE_APPEND) != 0;
+ File f = makeFilename(getFilesDir(), name);
+ try {
+ FileOutputStream fos = new FileOutputStream(f, append);
+ setFilePermissionsFromMode(f.getPath(), mode, 0);
+ return fos;
+ } catch (FileNotFoundException e) {
+ }
+
+ File parent = f.getParentFile();
+ parent.mkdir();
+ FileUtils.setPermissions(
+ parent.getPath(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+ -1, -1);
+ FileOutputStream fos = new FileOutputStream(f, append);
+ setFilePermissionsFromMode(f.getPath(), mode, 0);
+ return fos;
+ }
+
+ @Override
+ public boolean deleteFile(String name) {
+ File f = makeFilename(getFilesDir(), name);
+ return f.delete();
+ }
+
+ @Override
+ public File getFilesDir() {
+ synchronized (mSync) {
+ if (mFilesDir == null) {
+ mFilesDir = new File(getDataDirFile(), "files");
+ }
+ if (!mFilesDir.exists()) {
+ if(!mFilesDir.mkdirs()) {
+ Log.w(TAG, "Unable to create files directory");
+ return null;
+ }
+ FileUtils.setPermissions(
+ mFilesDir.getPath(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+ -1, -1);
+ }
+ return mFilesDir;
+ }
+ }
+
+ @Override
+ public File getCacheDir() {
+ synchronized (mSync) {
+ if (mCacheDir == null) {
+ mCacheDir = new File(getDataDirFile(), "cache");
+ }
+ if (!mCacheDir.exists()) {
+ if(!mCacheDir.mkdirs()) {
+ Log.w(TAG, "Unable to create cache directory");
+ return null;
+ }
+ FileUtils.setPermissions(
+ mCacheDir.getPath(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+ -1, -1);
+ }
+ }
+ return mCacheDir;
+ }
+
+
+ @Override
+ public File getFileStreamPath(String name) {
+ return makeFilename(getFilesDir(), name);
+ }
+
+ @Override
+ public String[] fileList() {
+ final String[] list = getFilesDir().list();
+ return (list != null) ? list : EMPTY_FILE_LIST;
+ }
+
+ @Override
+ public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
+ File dir = getDatabasesDir();
+ if (!dir.isDirectory() && dir.mkdir()) {
+ FileUtils.setPermissions(dir.getPath(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+ -1, -1);
+ }
+
+ File f = makeFilename(dir, name);
+ SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, factory);
+ setFilePermissionsFromMode(f.getPath(), mode, 0);
+ return db;
+ }
+
+ @Override
+ public boolean deleteDatabase(String name) {
+ try {
+ File f = makeFilename(getDatabasesDir(), name);
+ return f.delete();
+ } catch (Exception e) {
+ }
+ return false;
+ }
+
+ @Override
+ public File getDatabasePath(String name) {
+ return makeFilename(getDatabasesDir(), name);
+ }
+
+ @Override
+ public String[] databaseList() {
+ final String[] list = getDatabasesDir().list();
+ return (list != null) ? list : EMPTY_FILE_LIST;
+ }
+
+
+ private File getDatabasesDir() {
+ synchronized (mSync) {
+ if (mDatabasesDir == null) {
+ mDatabasesDir = new File(getDataDirFile(), "databases");
+ }
+ if (mDatabasesDir.getPath().equals("databases")) {
+ mDatabasesDir = new File("/data/system");
+ }
+ return mDatabasesDir;
+ }
+ }
+
+ @Override
+ public Drawable getWallpaper() {
+ Drawable dr = peekWallpaper();
+ return dr != null ? dr : getResources().getDrawable(
+ com.android.internal.R.drawable.default_wallpaper);
+ }
+
+ @Override
+ public synchronized Drawable peekWallpaper() {
+ if (mWallpaper != null) {
+ return mWallpaper;
+ }
+ mWallpaperCallback = new WallpaperCallback(this);
+ mWallpaper = getCurrentWallpaperLocked();
+ return mWallpaper;
+ }
+
+ private Drawable getCurrentWallpaperLocked() {
+ try {
+ ParcelFileDescriptor fd = getWallpaperService().getWallpaper(mWallpaperCallback);
+ if (fd != null) {
+ Bitmap bm = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor());
+ if (bm != null) {
+ return new BitmapDrawable(bm);
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ @Override
+ public int getWallpaperDesiredMinimumWidth() {
+ try {
+ return getWallpaperService().getWidthHint();
+ } catch (RemoteException e) {
+ // Shouldn't happen!
+ return 0;
+ }
+ }
+
+ @Override
+ public int getWallpaperDesiredMinimumHeight() {
+ try {
+ return getWallpaperService().getHeightHint();
+ } catch (RemoteException e) {
+ // Shouldn't happen!
+ return 0;
+ }
+ }
+
+ @Override
+ public void setWallpaper(Bitmap bitmap) throws IOException {
+ try {
+ ParcelFileDescriptor fd = getWallpaperService().setWallpaper();
+ if (fd == null) {
+ return;
+ }
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void setWallpaper(InputStream data) throws IOException {
+ try {
+ ParcelFileDescriptor fd = getWallpaperService().setWallpaper();
+ if (fd == null) {
+ return;
+ }
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ setWallpaper(data, fos);
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void setWallpaper(InputStream data, FileOutputStream fos)
+ throws IOException {
+ byte[] buffer = new byte[32768];
+ int amt;
+ while ((amt=data.read(buffer)) > 0) {
+ fos.write(buffer, 0, amt);
+ }
+ }
+
+ @Override
+ public void clearWallpaper() throws IOException {
+ try {
+ /* Set the wallpaper to the default values */
+ ParcelFileDescriptor fd = getWallpaperService().setWallpaper();
+ if (fd != null) {
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ setWallpaper(getResources().openRawResource(
+ com.android.internal.R.drawable.default_wallpaper),
+ fos);
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void startActivity(Intent intent) {
+ if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ throw new AndroidRuntimeException(
+ "Calling startActivity() from outside of an Activity "
+ + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ + " Is this really what you want?");
+ }
+ mMainThread.getInstrumentation().execStartActivity(
+ getOuterContext(), mMainThread.getApplicationThread(), null, null, intent, -1);
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent) {
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, null,
+ Activity.RESULT_OK, null, null, null, false, false);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent, String receiverPermission) {
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, null,
+ Activity.RESULT_OK, null, null, receiverPermission, false, false);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent,
+ String receiverPermission) {
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, null,
+ Activity.RESULT_OK, null, null, receiverPermission, true, false);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent,
+ String receiverPermission, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData,
+ Bundle initialExtras) {
+ IIntentReceiver rd = null;
+ if (resultReceiver != null) {
+ if (mPackageInfo != null) {
+ if (scheduler == null) {
+ scheduler = mMainThread.getHandler();
+ }
+ rd = mPackageInfo.getReceiverDispatcher(
+ resultReceiver, getOuterContext(), scheduler,
+ mMainThread.getInstrumentation(), false);
+ } else {
+ if (scheduler == null) {
+ scheduler = mMainThread.getHandler();
+ }
+ rd = new ActivityThread.PackageInfo.ReceiverDispatcher(
+ resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
+ }
+ }
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, rd,
+ initialCode, initialData, initialExtras, receiverPermission,
+ true, false);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void sendStickyBroadcast(Intent intent) {
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, null,
+ Activity.RESULT_OK, null, null, null, false, true);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void removeStickyBroadcast(Intent intent) {
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ if (resolvedType != null) {
+ intent = new Intent(intent);
+ intent.setDataAndType(intent.getData(), resolvedType);
+ }
+ try {
+ ActivityManagerNative.getDefault().unbroadcastIntent(
+ mMainThread.getApplicationThread(), intent);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ return registerReceiver(receiver, filter, null, null);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ return registerReceiverInternal(receiver, filter, broadcastPermission,
+ scheduler, getOuterContext());
+ }
+
+ private Intent registerReceiverInternal(BroadcastReceiver receiver,
+ IntentFilter filter, String broadcastPermission,
+ Handler scheduler, Context context) {
+ IIntentReceiver rd = null;
+ if (receiver != null) {
+ if (mPackageInfo != null && context != null) {
+ if (scheduler == null) {
+ scheduler = mMainThread.getHandler();
+ }
+ rd = mPackageInfo.getReceiverDispatcher(
+ receiver, context, scheduler,
+ mMainThread.getInstrumentation(), true);
+ } else {
+ if (scheduler == null) {
+ scheduler = mMainThread.getHandler();
+ }
+ rd = new ActivityThread.PackageInfo.ReceiverDispatcher(
+ receiver, context, scheduler, null, false).getIIntentReceiver();
+ }
+ }
+ try {
+ return ActivityManagerNative.getDefault().registerReceiver(
+ mMainThread.getApplicationThread(),
+ rd, filter, broadcastPermission);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ if (mPackageInfo != null) {
+ IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
+ getOuterContext(), receiver);
+ try {
+ ActivityManagerNative.getDefault().unregisterReceiver(rd);
+ } catch (RemoteException e) {
+ }
+ } else {
+ throw new RuntimeException("Not supported in system context");
+ }
+ }
+
+ @Override
+ public ComponentName startService(Intent service) {
+ try {
+ ComponentName cn = ActivityManagerNative.getDefault().startService(
+ mMainThread.getApplicationThread(), service,
+ service.resolveTypeIfNeeded(getContentResolver()));
+ if (cn != null && cn.getPackageName().equals("!")) {
+ throw new SecurityException(
+ "Not allowed to start service " + service
+ + " without permission " + cn.getClassName());
+ }
+ return cn;
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean stopService(Intent service) {
+ try {
+ int res = ActivityManagerNative.getDefault().stopService(
+ mMainThread.getApplicationThread(), service,
+ service.resolveTypeIfNeeded(getContentResolver()));
+ if (res < 0) {
+ throw new SecurityException(
+ "Not allowed to stop service " + service);
+ }
+ return res != 0;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean bindService(Intent service, ServiceConnection conn,
+ int flags) {
+ IServiceConnection sd;
+ if (mPackageInfo != null) {
+ sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
+ mMainThread.getHandler(), flags);
+ } else {
+ throw new RuntimeException("Not supported in system context");
+ }
+ try {
+ int res = ActivityManagerNative.getDefault().bindService(
+ mMainThread.getApplicationThread(), getActivityToken(),
+ service, service.resolveTypeIfNeeded(getContentResolver()),
+ sd, flags);
+ if (res < 0) {
+ throw new SecurityException(
+ "Not allowed to bind to service " + service);
+ }
+ return res != 0;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void unbindService(ServiceConnection conn) {
+ if (mPackageInfo != null) {
+ IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+ getOuterContext(), conn);
+ try {
+ ActivityManagerNative.getDefault().unbindService(sd);
+ } catch (RemoteException e) {
+ }
+ } else {
+ throw new RuntimeException("Not supported in system context");
+ }
+ }
+
+ @Override
+ public boolean startInstrumentation(ComponentName className,
+ String profileFile, Bundle arguments) {
+ try {
+ return ActivityManagerNative.getDefault().startInstrumentation(
+ className, profileFile, 0, arguments, null);
+ } catch (RemoteException e) {
+ // System has crashed, nothing we can do.
+ }
+ return false;
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (WINDOW_SERVICE.equals(name)) {
+ return WindowManagerImpl.getDefault();
+ } else if (LAYOUT_INFLATER_SERVICE.equals(name)) {
+ synchronized (mSync) {
+ LayoutInflater inflater = mLayoutInflater;
+ if (inflater != null) {
+ return inflater;
+ }
+ mLayoutInflater = inflater =
+ PolicyManager.makeNewLayoutInflater(getOuterContext());
+ return inflater;
+ }
+ } else if (ACTIVITY_SERVICE.equals(name)) {
+ return getActivityManager();
+ } else if (ALARM_SERVICE.equals(name)) {
+ return getAlarmManager();
+ } else if (POWER_SERVICE.equals(name)) {
+ return getPowerManager();
+ } else if (CONNECTIVITY_SERVICE.equals(name)) {
+ return getConnectivityManager();
+ } else if (WIFI_SERVICE.equals(name)) {
+ return getWifiManager();
+ } else if (NOTIFICATION_SERVICE.equals(name)) {
+ return getNotificationManager();
+ } else if (KEYGUARD_SERVICE.equals(name)) {
+ return new KeyguardManager();
+ } else if (LOCATION_SERVICE.equals(name)) {
+ return getLocationManager();
+ } else if (SEARCH_SERVICE.equals(name)) {
+ return getSearchManager();
+ } else if ( SENSOR_SERVICE.equals(name)) {
+ return getSensorManager();
+ } else if (BLUETOOTH_SERVICE.equals(name)) {
+ return getBluetoothDevice();
+ } else if (VIBRATOR_SERVICE.equals(name)) {
+ return getVibrator();
+ } else if (STATUS_BAR_SERVICE.equals(name)) {
+ synchronized (mSync) {
+ if (mStatusBarManager == null) {
+ mStatusBarManager = new StatusBarManager(getOuterContext());
+ }
+ return mStatusBarManager;
+ }
+ } else if (AUDIO_SERVICE.equals(name)) {
+ return getAudioManager();
+ } else if (TELEPHONY_SERVICE.equals(name)) {
+ return getTelephonyManager();
+ } else if (CLIPBOARD_SERVICE.equals(name)) {
+ return getClipboardManager();
+ } else if (INPUT_METHOD_SERVICE.equals(name)) {
+ return InputMethodManager.getInstance(this);
+ }
+
+ return null;
+ }
+
+ private ActivityManager getActivityManager() {
+ synchronized (mSync) {
+ if (mActivityManager == null) {
+ mActivityManager = new ActivityManager(getOuterContext(),
+ mMainThread.getHandler());
+ }
+ }
+ return mActivityManager;
+ }
+
+ private AlarmManager getAlarmManager() {
+ synchronized (sSync) {
+ if (sAlarmManager == null) {
+ IBinder b = ServiceManager.getService(ALARM_SERVICE);
+ IAlarmManager service = IAlarmManager.Stub.asInterface(b);
+ sAlarmManager = new AlarmManager(service);
+ }
+ }
+ return sAlarmManager;
+ }
+
+ private PowerManager getPowerManager() {
+ synchronized (sSync) {
+ if (sPowerManager == null) {
+ IBinder b = ServiceManager.getService(POWER_SERVICE);
+ IPowerManager service = IPowerManager.Stub.asInterface(b);
+ sPowerManager = new PowerManager(service, mMainThread.getHandler());
+ }
+ }
+ return sPowerManager;
+ }
+
+ private ConnectivityManager getConnectivityManager()
+ {
+ synchronized (sSync) {
+ if (sConnectivityManager == null) {
+ IBinder b = ServiceManager.getService(CONNECTIVITY_SERVICE);
+ IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ sConnectivityManager = new ConnectivityManager(service);
+ }
+ }
+ return sConnectivityManager;
+ }
+
+ private WifiManager getWifiManager()
+ {
+ synchronized (sSync) {
+ if (sWifiManager == null) {
+ IBinder b = ServiceManager.getService(WIFI_SERVICE);
+ IWifiManager service = IWifiManager.Stub.asInterface(b);
+ sWifiManager = new WifiManager(service, mMainThread.getHandler());
+ }
+ }
+ return sWifiManager;
+ }
+
+ private NotificationManager getNotificationManager()
+ {
+ synchronized (mSync) {
+ if (mNotificationManager == null) {
+ mNotificationManager = new NotificationManager(
+ new ContextThemeWrapper(getOuterContext(), com.android.internal.R.style.Theme_Dialog),
+ mMainThread.getHandler());
+ }
+ }
+ return mNotificationManager;
+ }
+
+ private TelephonyManager getTelephonyManager() {
+ synchronized (mSync) {
+ if (mTelephonyManager == null) {
+ mTelephonyManager = new TelephonyManager(getOuterContext());
+ }
+ }
+ return mTelephonyManager;
+ }
+
+ private ClipboardManager getClipboardManager() {
+ synchronized (mSync) {
+ if (mClipboardManager == null) {
+ mClipboardManager = new ClipboardManager(getOuterContext(),
+ mMainThread.getHandler());
+ }
+ }
+ return mClipboardManager;
+ }
+
+ private LocationManager getLocationManager() {
+ synchronized (sSync) {
+ if (sLocationManager == null) {
+ IBinder b = ServiceManager.getService(LOCATION_SERVICE);
+ ILocationManager service = ILocationManager.Stub.asInterface(b);
+ sLocationManager = new LocationManager(service);
+ }
+ }
+ return sLocationManager;
+ }
+
+ private SearchManager getSearchManager() {
+ // This is only useable in Activity Contexts
+ if (getActivityToken() == null) {
+ throw new AndroidRuntimeException(
+ "Acquiring SearchManager objects only valid in Activity Contexts.");
+ }
+ synchronized (mSync) {
+ if (mSearchManager == null) {
+ mSearchManager = new SearchManager(getOuterContext(), mMainThread.getHandler());
+ }
+ }
+ return mSearchManager;
+ }
+
+ private BluetoothDevice getBluetoothDevice() {
+ if (sIsBluetoothDeviceCached) {
+ return sBluetoothDevice;
+ }
+ synchronized (sSync) {
+ IBinder b = ServiceManager.getService(BLUETOOTH_SERVICE);
+ if (b == null) {
+ sBluetoothDevice = null;
+ } else {
+ IBluetoothDevice service = IBluetoothDevice.Stub.asInterface(b);
+ sBluetoothDevice = new BluetoothDevice(service);
+ }
+ sIsBluetoothDeviceCached = true;
+ }
+ return sBluetoothDevice;
+ }
+
+ private SensorManager getSensorManager() {
+ synchronized (mSync) {
+ if (mSensorManager == null) {
+ mSensorManager = new SensorManager(mMainThread.getHandler().getLooper());
+ }
+ }
+ return mSensorManager;
+ }
+
+ private Vibrator getVibrator() {
+ synchronized (mSync) {
+ if (mVibrator == null) {
+ mVibrator = new Vibrator();
+ }
+ }
+ return mVibrator;
+ }
+
+ private IWallpaperService getWallpaperService() {
+ synchronized (sSync) {
+ if (sWallpaperService == null) {
+ IBinder b = ServiceManager.getService(WALLPAPER_SERVICE);
+ sWallpaperService = IWallpaperService.Stub.asInterface(b);
+ }
+ }
+ return sWallpaperService;
+ }
+
+ private AudioManager getAudioManager()
+ {
+ if (mAudioManager == null) {
+ mAudioManager = new AudioManager(this);
+ }
+ return mAudioManager;
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid) {
+ if (permission == null) {
+ throw new IllegalArgumentException("permission is null");
+ }
+
+ if (!Process.supportsProcesses()) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ try {
+ return ActivityManagerNative.getDefault().checkPermission(
+ permission, pid, uid);
+ } catch (RemoteException e) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }
+
+ @Override
+ public int checkCallingPermission(String permission) {
+ if (permission == null) {
+ throw new IllegalArgumentException("permission is null");
+ }
+
+ if (!Process.supportsProcesses()) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ int pid = Binder.getCallingPid();
+ if (pid != Process.myPid()) {
+ return checkPermission(permission, pid,
+ Binder.getCallingUid());
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ if (permission == null) {
+ throw new IllegalArgumentException("permission is null");
+ }
+
+ return checkPermission(permission, Binder.getCallingPid(),
+ Binder.getCallingUid());
+ }
+
+ private void enforce(
+ String permission, int resultOfCheck,
+ boolean selfToo, int uid, String message) {
+ if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ (message != null ? (message + ": ") : "") +
+ (selfToo
+ ? "Neither user " + uid + " nor current process has "
+ : "User " + uid + " does not have ") +
+ permission +
+ ".");
+ }
+ }
+
+ public void enforcePermission(
+ String permission, int pid, int uid, String message) {
+ enforce(permission,
+ checkPermission(permission, pid, uid),
+ false,
+ uid,
+ message);
+ }
+
+ public void enforceCallingPermission(String permission, String message) {
+ enforce(permission,
+ checkCallingPermission(permission),
+ false,
+ Binder.getCallingUid(),
+ message);
+ }
+
+ public void enforceCallingOrSelfPermission(
+ String permission, String message) {
+ enforce(permission,
+ checkCallingOrSelfPermission(permission),
+ true,
+ Binder.getCallingUid(),
+ message);
+ }
+
+ @Override
+ public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
+ try {
+ ActivityManagerNative.getDefault().grantUriPermission(
+ mMainThread.getApplicationThread(), toPackage, uri,
+ modeFlags);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void revokeUriPermission(Uri uri, int modeFlags) {
+ try {
+ ActivityManagerNative.getDefault().revokeUriPermission(
+ mMainThread.getApplicationThread(), uri,
+ modeFlags);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
+ if (!Process.supportsProcesses()) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ try {
+ return ActivityManagerNative.getDefault().checkUriPermission(
+ uri, pid, uid, modeFlags);
+ } catch (RemoteException e) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }
+
+ @Override
+ public int checkCallingUriPermission(Uri uri, int modeFlags) {
+ if (!Process.supportsProcesses()) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ int pid = Binder.getCallingPid();
+ if (pid != Process.myPid()) {
+ return checkUriPermission(uri, pid,
+ Binder.getCallingUid(), modeFlags);
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ @Override
+ public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) {
+ return checkUriPermission(uri, Binder.getCallingPid(),
+ Binder.getCallingUid(), modeFlags);
+ }
+
+ @Override
+ public int checkUriPermission(Uri uri, String readPermission,
+ String writePermission, int pid, int uid, int modeFlags) {
+ if (false) {
+ Log.i("foo", "checkUriPermission: uri=" + uri + "readPermission="
+ + readPermission + " writePermission=" + writePermission
+ + " pid=" + pid + " uid=" + uid + " mode" + modeFlags);
+ }
+ if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ if (readPermission == null
+ || checkPermission(readPermission, pid, uid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if (writePermission == null
+ || checkPermission(writePermission, pid, uid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ return uri != null ? checkUriPermission(uri, pid, uid, modeFlags)
+ : PackageManager.PERMISSION_DENIED;
+ }
+
+ private String uriModeFlagToString(int uriModeFlags) {
+ switch (uriModeFlags) {
+ case Intent.FLAG_GRANT_READ_URI_PERMISSION |
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION:
+ return "read and write";
+ case Intent.FLAG_GRANT_READ_URI_PERMISSION:
+ return "read";
+ case Intent.FLAG_GRANT_WRITE_URI_PERMISSION:
+ return "write";
+ }
+ throw new IllegalArgumentException(
+ "Unknown permission mode flags: " + uriModeFlags);
+ }
+
+ private void enforceForUri(
+ int modeFlags, int resultOfCheck, boolean selfToo,
+ int uid, Uri uri, String message) {
+ if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ (message != null ? (message + ": ") : "") +
+ (selfToo
+ ? "Neither user " + uid + " nor current process has "
+ : "User " + uid + " does not have ") +
+ uriModeFlagToString(modeFlags) +
+ " permission on " +
+ uri +
+ ".");
+ }
+ }
+
+ public void enforceUriPermission(
+ Uri uri, int pid, int uid, int modeFlags, String message) {
+ enforceForUri(
+ modeFlags, checkUriPermission(uri, pid, uid, modeFlags),
+ false, uid, uri, message);
+ }
+
+ public void enforceCallingUriPermission(
+ Uri uri, int modeFlags, String message) {
+ enforceForUri(
+ modeFlags, checkCallingUriPermission(uri, modeFlags),
+ false, Binder.getCallingUid(), uri, message);
+ }
+
+ public void enforceCallingOrSelfUriPermission(
+ Uri uri, int modeFlags, String message) {
+ enforceForUri(
+ modeFlags,
+ checkCallingOrSelfUriPermission(uri, modeFlags), true,
+ Binder.getCallingUid(), uri, message);
+ }
+
+ public void enforceUriPermission(
+ Uri uri, String readPermission, String writePermission,
+ int pid, int uid, int modeFlags, String message) {
+ enforceForUri(modeFlags,
+ checkUriPermission(
+ uri, readPermission, writePermission, pid, uid,
+ modeFlags),
+ false,
+ uid,
+ uri,
+ message);
+ }
+
+ @Override
+ public Context createPackageContext(String packageName, int flags)
+ throws PackageManager.NameNotFoundException {
+ if (packageName.equals("system") || packageName.equals("android")) {
+ return new ApplicationContext(mMainThread.getSystemContext());
+ }
+
+ ActivityThread.PackageInfo pi =
+ mMainThread.getPackageInfo(packageName, flags);
+ if (pi != null) {
+ ApplicationContext c = new ApplicationContext();
+ c.init(pi, null, mMainThread);
+ if (c.mResources != null) {
+ return c;
+ }
+ }
+
+ // Should be a better exception.
+ throw new PackageManager.NameNotFoundException(
+ "Application package " + packageName + " not found");
+ }
+
+ private File getDataDirFile() {
+ if (mPackageInfo != null) {
+ return mPackageInfo.getDataDirFile();
+ }
+ throw new RuntimeException("Not supported in system context");
+ }
+
+ @Override
+ public File getDir(String name, int mode) {
+ name = "app_" + name;
+ File file = makeFilename(getDataDirFile(), name);
+ if (!file.exists()) {
+ file.mkdir();
+ setFilePermissionsFromMode(file.getPath(), mode,
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH);
+ }
+ return file;
+ }
+
+ static ApplicationContext createSystemContext(ActivityThread mainThread) {
+ ApplicationContext context = new ApplicationContext();
+ context.init(Resources.getSystem(), mainThread);
+ return context;
+ }
+
+ ApplicationContext() {
+ ++sInstanceCount;
+ mOuterContext = this;
+ }
+
+ /**
+ * Create a new ApplicationContext from an existing one. The new one
+ * works and operates the same as the one it is copying.
+ *
+ * @param context Existing application context.
+ */
+ public ApplicationContext(ApplicationContext context) {
+ ++sInstanceCount;
+ mPackageInfo = context.mPackageInfo;
+ mResources = context.mResources;
+ mMainThread = context.mMainThread;
+ mContentResolver = context.mContentResolver;
+ mOuterContext = this;
+ }
+
+ final void init(ActivityThread.PackageInfo packageInfo,
+ IBinder activityToken, ActivityThread mainThread) {
+ mPackageInfo = packageInfo;
+ mResources = mPackageInfo.getResources(mainThread);
+ mMainThread = mainThread;
+ mContentResolver = new ApplicationContentResolver(this, mainThread);
+
+ setActivityToken(activityToken);
+ }
+
+ final void init(Resources resources, ActivityThread mainThread) {
+ mPackageInfo = null;
+ mResources = resources;
+ mMainThread = mainThread;
+ mContentResolver = new ApplicationContentResolver(this, mainThread);
+ }
+
+ final void scheduleFinalCleanup(String who, String what) {
+ mMainThread.scheduleContextCleanup(this, who, what);
+ }
+
+ final void performFinalCleanup(String who, String what) {
+ //Log.i(TAG, "Cleanup up context: " + this);
+ mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+ }
+
+ final Context getReceiverRestrictedContext() {
+ if (mReceiverRestrictedContext != null) {
+ return mReceiverRestrictedContext;
+ }
+ return mReceiverRestrictedContext = new ReceiverRestrictedContext(getOuterContext());
+ }
+
+ final void setActivityToken(IBinder token) {
+ mActivityToken = token;
+ }
+
+ final void setOuterContext(Context context) {
+ mOuterContext = context;
+ }
+
+ final Context getOuterContext() {
+ return mOuterContext;
+ }
+
+ final IBinder getActivityToken() {
+ return mActivityToken;
+ }
+
+ private static void setFilePermissionsFromMode(String name, int mode,
+ int extraPermissions) {
+ int perms = FileUtils.S_IRUSR|FileUtils.S_IWUSR
+ |FileUtils.S_IRGRP|FileUtils.S_IWGRP
+ |extraPermissions;
+ if ((mode&MODE_WORLD_READABLE) != 0) {
+ perms |= FileUtils.S_IROTH;
+ }
+ if ((mode&MODE_WORLD_WRITEABLE) != 0) {
+ perms |= FileUtils.S_IWOTH;
+ }
+ if (false) {
+ Log.i(TAG, "File " + name + ": mode=0x" + Integer.toHexString(mode)
+ + ", perms=0x" + Integer.toHexString(perms));
+ }
+ FileUtils.setPermissions(name, perms, -1, -1);
+ }
+
+ private File makeFilename(File base, String name) {
+ if (name.indexOf(File.separatorChar) < 0) {
+ return new File(base, name);
+ }
+ throw new IllegalArgumentException(
+ "File " + name + " contains a path separator");
+ }
+
+ // ----------------------------------------------------------------------
+ // ----------------------------------------------------------------------
+ // ----------------------------------------------------------------------
+
+ private static final class ApplicationContentResolver extends ContentResolver {
+ public ApplicationContentResolver(Context context,
+ ActivityThread mainThread)
+ {
+ super(context);
+ mMainThread = mainThread;
+ }
+
+ @Override
+ protected IContentProvider acquireProvider(Context context, String name)
+ {
+ return mMainThread.acquireProvider(context, name);
+ }
+
+ @Override
+ public boolean releaseProvider(IContentProvider provider)
+ {
+ return mMainThread.releaseProvider(provider);
+ }
+
+ private final ActivityThread mMainThread;
+ }
+
+ // ----------------------------------------------------------------------
+ // ----------------------------------------------------------------------
+ // ----------------------------------------------------------------------
+
+ /*package*/
+ static final class ApplicationPackageManager extends PackageManager {
+ @Override
+ public PackageInfo getPackageInfo(String packageName, int flags)
+ throws NameNotFoundException {
+ try {
+ PackageInfo pi = mPM.getPackageInfo(packageName, flags);
+ if (pi != null) {
+ return pi;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(packageName);
+ }
+
+ public Intent getLaunchIntentForPackage(String packageName)
+ throws NameNotFoundException {
+ // First see if the package has an INFO activity; the existence of
+ // such an activity is implied to be the desired front-door for the
+ // overall package (such as if it has multiple launcher entries).
+ Intent intent = getLaunchIntentForPackageCategory(this, packageName,
+ Intent.CATEGORY_INFO);
+ if (intent != null) {
+ return intent;
+ }
+
+ // Otherwise, try to find a main launcher activity.
+ return getLaunchIntentForPackageCategory(this, packageName,
+ Intent.CATEGORY_LAUNCHER);
+ }
+
+ // XXX This should be implemented as a call to the package manager,
+ // to reduce the work needed.
+ static Intent getLaunchIntentForPackageCategory(PackageManager pm,
+ String packageName, String category) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent intentToResolve = new Intent(Intent.ACTION_MAIN, null);
+ intentToResolve.addCategory(category);
+ final List<ResolveInfo> apps =
+ pm.queryIntentActivities(intentToResolve, 0);
+ // I wish there were a way to directly get the "main" activity of a
+ // package but ...
+ for (ResolveInfo app : apps) {
+ if (app.activityInfo.packageName.equals(packageName)) {
+ intent.setClassName(packageName, app.activityInfo.name);
+ return intent;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public int[] getPackageGids(String packageName)
+ throws NameNotFoundException {
+ try {
+ int[] gids = mPM.getPackageGids(packageName);
+ if (gids == null || gids.length > 0) {
+ return gids;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(packageName);
+ }
+
+ @Override
+ public PermissionInfo getPermissionInfo(String name, int flags)
+ throws NameNotFoundException {
+ try {
+ PermissionInfo pi = mPM.getPermissionInfo(name, flags);
+ if (pi != null) {
+ return pi;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(name);
+ }
+
+ @Override
+ public List<PermissionInfo> queryPermissionsByGroup(String group, int flags)
+ throws NameNotFoundException {
+ try {
+ List<PermissionInfo> pi = mPM.queryPermissionsByGroup(group, flags);
+ if (pi != null) {
+ return pi;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(group);
+ }
+
+ @Override
+ public PermissionGroupInfo getPermissionGroupInfo(String name,
+ int flags) throws NameNotFoundException {
+ try {
+ PermissionGroupInfo pgi = mPM.getPermissionGroupInfo(name, flags);
+ if (pgi != null) {
+ return pgi;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(name);
+ }
+
+ @Override
+ public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
+ try {
+ return mPM.getAllPermissionGroups(flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public ApplicationInfo getApplicationInfo(String packageName, int flags)
+ throws NameNotFoundException {
+ try {
+ ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags);
+ if (ai != null) {
+ return ai;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(packageName);
+ }
+
+ @Override
+ public ActivityInfo getActivityInfo(ComponentName className, int flags)
+ throws NameNotFoundException {
+ try {
+ ActivityInfo ai = mPM.getActivityInfo(className, flags);
+ if (ai != null) {
+ return ai;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(className.toString());
+ }
+
+ @Override
+ public ActivityInfo getReceiverInfo(ComponentName className, int flags)
+ throws NameNotFoundException {
+ try {
+ ActivityInfo ai = mPM.getReceiverInfo(className, flags);
+ if (ai != null) {
+ return ai;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(className.toString());
+ }
+
+ @Override
+ public ServiceInfo getServiceInfo(ComponentName className, int flags)
+ throws NameNotFoundException {
+ try {
+ ServiceInfo si = mPM.getServiceInfo(className, flags);
+ if (si != null) {
+ return si;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(className.toString());
+ }
+
+ @Override
+ public String[] getSystemSharedLibraryNames() {
+ try {
+ return mPM.getSystemSharedLibraryNames();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public int checkPermission(String permName, String pkgName) {
+ try {
+ return mPM.checkPermission(permName, pkgName);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public boolean addPermission(PermissionInfo info) {
+ try {
+ return mPM.addPermission(info);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public void removePermission(String name) {
+ try {
+ mPM.removePermission(name);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public int checkSignatures(String pkg1, String pkg2) {
+ try {
+ return mPM.checkSignatures(pkg1, pkg2);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public String[] getPackagesForUid(int uid) {
+ try {
+ return mPM.getPackagesForUid(uid);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public String getNameForUid(int uid) {
+ try {
+ return mPM.getNameForUid(uid);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public int getUidForSharedUser(String sharedUserName)
+ throws NameNotFoundException {
+ try {
+ int uid = mPM.getUidForSharedUser(sharedUserName);
+ if(uid != -1) {
+ return uid;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ throw new NameNotFoundException("No shared userid for user:"+sharedUserName);
+ }
+
+ @Override
+ public List<PackageInfo> getInstalledPackages(int flags) {
+ try {
+ return mPM.getInstalledPackages(flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public List<ApplicationInfo> getInstalledApplications(int flags) {
+ try {
+ return mPM.getInstalledApplications(flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public ResolveInfo resolveActivity(Intent intent, int flags) {
+ try {
+ return mPM.resolveIntent(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public List<ResolveInfo> queryIntentActivities(Intent intent,
+ int flags) {
+ try {
+ return mPM.queryIntentActivities(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public List<ResolveInfo> queryIntentActivityOptions(
+ ComponentName caller, Intent[] specifics, Intent intent,
+ int flags) {
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ String[] specificTypes = null;
+ if (specifics != null) {
+ final int N = specifics.length;
+ for (int i=0; i<N; i++) {
+ Intent sp = specifics[i];
+ if (sp != null) {
+ String t = sp.resolveTypeIfNeeded(resolver);
+ if (t != null) {
+ if (specificTypes == null) {
+ specificTypes = new String[N];
+ }
+ specificTypes[i] = t;
+ }
+ }
+ }
+ }
+
+ try {
+ return mPM.queryIntentActivityOptions(caller, specifics,
+ specificTypes, intent, intent.resolveTypeIfNeeded(resolver),
+ flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
+ try {
+ return mPM.queryIntentReceivers(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public ResolveInfo resolveService(Intent intent, int flags) {
+ try {
+ return mPM.resolveService(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
+ try {
+ return mPM.queryIntentServices(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public ProviderInfo resolveContentProvider(String name,
+ int flags) {
+ try {
+ return mPM.resolveContentProvider(name, flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public List<ProviderInfo> queryContentProviders(String processName,
+ int uid, int flags) {
+ try {
+ return mPM.queryContentProviders(processName, uid, flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public InstrumentationInfo getInstrumentationInfo(
+ ComponentName className, int flags)
+ throws NameNotFoundException {
+ try {
+ InstrumentationInfo ii = mPM.getInstrumentationInfo(
+ className, flags);
+ if (ii != null) {
+ return ii;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+
+ throw new NameNotFoundException(className.toString());
+ }
+
+ @Override
+ public List<InstrumentationInfo> queryInstrumentation(
+ String targetPackage, int flags) {
+ try {
+ return mPM.queryInstrumentation(targetPackage, flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override public Drawable getDrawable(String packageName, int resid,
+ ApplicationInfo appInfo) {
+ ResourceName name = new ResourceName(packageName, resid);
+ Drawable dr = getCachedIcon(name);
+ if (dr != null) {
+ return dr;
+ }
+ if (appInfo == null) {
+ try {
+ appInfo = getApplicationInfo(packageName, 0);
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ }
+ try {
+ Resources r = getResourcesForApplication(appInfo);
+ dr = r.getDrawable(resid);
+ if (DEBUG_ICONS) Log.v(TAG, "Getting drawable 0x"
+ + Integer.toHexString(resid) + " from " + r
+ + ": " + dr);
+ putCachedIcon(name, dr);
+ return dr;
+ } catch (NameNotFoundException e) {
+ Log.w("PackageManager", "Failure retrieving resources for"
+ + appInfo.packageName);
+ } catch (RuntimeException e) {
+ // If an exception was thrown, fall through to return
+ // default icon.
+ Log.w("PackageManager", "Failure retrieving icon 0x"
+ + Integer.toHexString(resid) + " in package "
+ + packageName, e);
+ }
+ return null;
+ }
+
+ @Override public Drawable getActivityIcon(ComponentName activityName)
+ throws NameNotFoundException {
+ return getActivityInfo(activityName, 0).loadIcon(this);
+ }
+
+ @Override public Drawable getActivityIcon(Intent intent)
+ throws NameNotFoundException {
+ if (intent.getComponent() != null) {
+ return getActivityIcon(intent.getComponent());
+ }
+
+ ResolveInfo info = resolveActivity(
+ intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (info != null) {
+ return info.activityInfo.loadIcon(this);
+ }
+
+ throw new NameNotFoundException(intent.toURI());
+ }
+
+ @Override public Drawable getDefaultActivityIcon() {
+ return Resources.getSystem().getDrawable(
+ com.android.internal.R.drawable.sym_def_app_icon);
+ }
+
+ @Override public Drawable getApplicationIcon(ApplicationInfo info) {
+ final int icon = info.icon;
+ if (icon != 0) {
+ ResourceName name = new ResourceName(info, icon);
+ Drawable dr = getCachedIcon(name);
+ if (dr != null) {
+ return dr;
+ }
+ try {
+ Resources r = getResourcesForApplication(info);
+ dr = r.getDrawable(icon);
+ if (DEBUG_ICONS) Log.v(TAG, "Getting drawable 0x"
+ + Integer.toHexString(icon) + " from " + r
+ + ": " + dr);
+ putCachedIcon(name, dr);
+ return dr;
+ } catch (NameNotFoundException e) {
+ Log.w("PackageManager", "Failure retrieving resources for"
+ + info.packageName);
+ } catch (RuntimeException e) {
+ // If an exception was thrown, fall through to return
+ // default icon.
+ Log.w("PackageManager", "Failure retrieving app icon", e);
+ }
+ }
+ return getDefaultActivityIcon();
+ }
+
+ @Override public Drawable getApplicationIcon(String packageName)
+ throws NameNotFoundException {
+ return getApplicationIcon(getApplicationInfo(packageName, 0));
+ }
+
+ @Override public Resources getResourcesForActivity(
+ ComponentName activityName) throws NameNotFoundException {
+ return getResourcesForApplication(
+ getActivityInfo(activityName, 0).applicationInfo);
+ }
+
+ @Override public Resources getResourcesForApplication(
+ ApplicationInfo app) throws NameNotFoundException {
+ if (app.packageName.equals("system")) {
+ return mContext.mMainThread.getSystemContext().getResources();
+ }
+ Resources r = mContext.mMainThread.getTopLevelResources(
+ app.uid == Process.myUid() ? app.sourceDir
+ : app.publicSourceDir);
+ if (r != null) {
+ return r;
+ }
+ throw new NameNotFoundException("Unable to open " + app.publicSourceDir);
+ }
+
+ @Override public Resources getResourcesForApplication(
+ String appPackageName) throws NameNotFoundException {
+ return getResourcesForApplication(
+ getApplicationInfo(appPackageName, 0));
+ }
+
+ int mCachedSafeMode = -1;
+ @Override public boolean isSafeMode() {
+ try {
+ if (mCachedSafeMode < 0) {
+ mCachedSafeMode = mPM.isSafeMode() ? 1 : 0;
+ }
+ return mCachedSafeMode != 0;
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ static void configurationChanged() {
+ synchronized (sSync) {
+ sIconCache.clear();
+ sStringCache.clear();
+ }
+ }
+
+ ApplicationPackageManager(ApplicationContext context,
+ IPackageManager pm) {
+ mContext = context;
+ mPM = pm;
+ }
+
+ private Drawable getCachedIcon(ResourceName name) {
+ synchronized (sSync) {
+ WeakReference<Drawable> wr = sIconCache.get(name);
+ if (DEBUG_ICONS) Log.v(TAG, "Get cached weak drawable ref for "
+ + name + ": " + wr);
+ if (wr != null) { // we have the activity
+ Drawable dr = wr.get();
+ if (dr != null) {
+ if (DEBUG_ICONS) Log.v(TAG, "Get cached drawable for "
+ + name + ": " + dr);
+ return dr;
+ }
+ // our entry has been purged
+ sIconCache.remove(name);
+ }
+ }
+ return null;
+ }
+
+ private void establishPackageRemovedReceiver() {
+ // mContext.registerReceiverInternal() winds up acquiring the
+ // main ActivityManagerService.this lock. If we hold our usual
+ // sSync global lock at the same time, we impose a required ordering
+ // on those two locks, which is not good for deadlock prevention.
+ // Use a dedicated lock around initialization of
+ // sPackageRemovedReceiver to avoid this.
+ synchronized (sPackageRemovedSync) {
+ if (sPackageRemovedReceiver == null) {
+ sPackageRemovedReceiver = new PackageRemovedReceiver();
+ IntentFilter filter = new IntentFilter(
+ Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverInternal(sPackageRemovedReceiver,
+ filter, null, null, null);
+ }
+ }
+ }
+
+ private void putCachedIcon(ResourceName name, Drawable dr) {
+ establishPackageRemovedReceiver();
+
+ synchronized (sSync) {
+ sIconCache.put(name, new WeakReference<Drawable>(dr));
+ if (DEBUG_ICONS) Log.v(TAG, "Added cached drawable for "
+ + name + ": " + dr);
+ }
+ }
+
+ private static final class PackageRemovedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Uri data = intent.getData();
+ String ssp;
+ if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
+ boolean needCleanup = false;
+ synchronized (sSync) {
+ Iterator<ResourceName> it = sIconCache.keySet().iterator();
+ while (it.hasNext()) {
+ ResourceName nm = it.next();
+ if (nm.packageName.equals(ssp)) {
+ //Log.i(TAG, "Removing cached drawable for " + nm);
+ it.remove();
+ needCleanup = true;
+ }
+ }
+ it = sStringCache.keySet().iterator();
+ while (it.hasNext()) {
+ ResourceName nm = it.next();
+ if (nm.packageName.equals(ssp)) {
+ //Log.i(TAG, "Removing cached string for " + nm);
+ it.remove();
+ needCleanup = true;
+ }
+ }
+ }
+ if (needCleanup || ActivityThread.currentActivityThread().hasPackageInfo(ssp)) {
+ ActivityThread.currentActivityThread().scheduleGcIdler();
+ }
+ }
+ }
+ }
+
+ private static final class ResourceName {
+ final String packageName;
+ final int iconId;
+
+ ResourceName(String _packageName, int _iconId) {
+ packageName = _packageName;
+ iconId = _iconId;
+ }
+
+ ResourceName(ApplicationInfo aInfo, int _iconId) {
+ this(aInfo.packageName, _iconId);
+ }
+
+ ResourceName(ComponentInfo cInfo, int _iconId) {
+ this(cInfo.applicationInfo.packageName, _iconId);
+ }
+
+ ResourceName(ResolveInfo rInfo, int _iconId) {
+ this(rInfo.activityInfo.applicationInfo.packageName, _iconId);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ResourceName that = (ResourceName) o;
+
+ if (iconId != that.iconId) return false;
+ return !(packageName != null ?
+ !packageName.equals(that.packageName) : that.packageName != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result;
+ result = packageName.hashCode();
+ result = 31 * result + iconId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "{ResourceName " + packageName + " / " + iconId + "}";
+ }
+ }
+
+ private CharSequence getCachedString(ResourceName name) {
+ synchronized (sSync) {
+ WeakReference<CharSequence> wr = sStringCache.get(name);
+ if (wr != null) { // we have the activity
+ CharSequence cs = wr.get();
+ if (cs != null) {
+ return cs;
+ }
+ // our entry has been purged
+ sStringCache.remove(name);
+ }
+ }
+ return null;
+ }
+
+ private void putCachedString(ResourceName name, CharSequence cs) {
+ establishPackageRemovedReceiver();
+
+ synchronized (sSync) {
+ sStringCache.put(name, new WeakReference<CharSequence>(cs));
+ }
+ }
+
+ private CharSequence getLabel(ResourceName name, ApplicationInfo app, int id) {
+ CharSequence cs = getCachedString(name);
+ if (cs != null) {
+ return cs;
+ }
+ try {
+ Resources r = getResourcesForApplication(app);
+ cs = r.getText(id);
+ putCachedString(name, cs);
+ } catch (NameNotFoundException e) {
+ Log.w("PackageManager", "Failure retrieving resources for"
+ + app.packageName);
+ } catch (RuntimeException e) {
+ // If an exception was thrown, fall through to return null
+ Log.w("ApplicationInfo", "Failure retrieving activity name", e);
+ }
+ return cs;
+ }
+
+ @Override
+ public CharSequence getText(String packageName, int resid,
+ ApplicationInfo appInfo) {
+ ResourceName name = new ResourceName(packageName, resid);
+ CharSequence text = getCachedString(name);
+ if (text != null) {
+ return text;
+ }
+ if (appInfo == null) {
+ try {
+ appInfo = getApplicationInfo(packageName, 0);
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ }
+ try {
+ Resources r = getResourcesForApplication(appInfo);
+ text = r.getText(resid);
+ putCachedString(name, text);
+ return text;
+ } catch (NameNotFoundException e) {
+ Log.w("PackageManager", "Failure retrieving resources for"
+ + appInfo.packageName);
+ } catch (RuntimeException e) {
+ // If an exception was thrown, fall through to return
+ // default icon.
+ Log.w("PackageManager", "Failure retrieving text 0x"
+ + Integer.toHexString(resid) + " in package "
+ + packageName, e);
+ }
+ return null;
+ }
+
+ @Override
+ public XmlResourceParser getXml(String packageName, int resid,
+ ApplicationInfo appInfo) {
+ if (appInfo == null) {
+ try {
+ appInfo = getApplicationInfo(packageName, 0);
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ }
+ try {
+ Resources r = getResourcesForApplication(appInfo);
+ return r.getXml(resid);
+ } catch (RuntimeException e) {
+ // If an exception was thrown, fall through to return
+ // default icon.
+ Log.w("PackageManager", "Failure retrieving xml 0x"
+ + Integer.toHexString(resid) + " in package "
+ + packageName, e);
+ } catch (NameNotFoundException e) {
+ Log.w("PackageManager", "Failure retrieving resources for"
+ + appInfo.packageName);
+ }
+ return null;
+ }
+
+ @Override
+ public CharSequence getApplicationLabel(ApplicationInfo info) {
+ if (info.nonLocalizedLabel != null) {
+ return info.nonLocalizedLabel;
+ }
+ final int id = info.labelRes;
+ if (id != 0) {
+ CharSequence cs = getLabel(new ResourceName(info, id), info, id);
+ if (cs != null) {
+ return cs;
+ }
+ }
+ return info.packageName;
+ }
+
+ @Override
+ public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags) {
+ try {
+ mPM.installPackage(packageURI, observer, flags);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
+ try {
+ mPM.deletePackage(packageName, observer, flags);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+ @Override
+ public void clearApplicationUserData(String packageName,
+ IPackageDataObserver observer) {
+ try {
+ mPM.clearApplicationUserData(packageName, observer);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+ @Override
+ public void deleteApplicationCacheFiles(String packageName,
+ IPackageDataObserver observer) {
+ try {
+ mPM.deleteApplicationCacheFiles(packageName, observer);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+ @Override
+ public void freeStorageAndNotify(long idealStorageSize, IPackageDataObserver observer) {
+ try {
+ mPM.freeStorageAndNotify(idealStorageSize, observer);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public void freeStorage(long idealStorageSize, PendingIntent opFinishedIntent) {
+ try {
+ mPM.freeStorage(idealStorageSize, opFinishedIntent);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public void getPackageSizeInfo(String packageName,
+ IPackageStatsObserver observer) {
+ try {
+ mPM.getPackageSizeInfo(packageName, observer);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+ @Override
+ public void addPackageToPreferred(String packageName) {
+ try {
+ mPM.addPackageToPreferred(packageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public void removePackageFromPreferred(String packageName) {
+ try {
+ mPM.removePackageFromPreferred(packageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public List<PackageInfo> getPreferredPackages(int flags) {
+ try {
+ return mPM.getPreferredPackages(flags);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return new ArrayList<PackageInfo>();
+ }
+
+ @Override
+ public void addPreferredActivity(IntentFilter filter,
+ int match, ComponentName[] set, ComponentName activity) {
+ try {
+ mPM.addPreferredActivity(filter, match, set, activity);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public void clearPackagePreferredActivities(String packageName) {
+ try {
+ mPM.clearPackagePreferredActivities(packageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public int getPreferredActivities(List<IntentFilter> outFilters,
+ List<ComponentName> outActivities, String packageName) {
+ try {
+ return mPM.getPreferredActivities(outFilters, outActivities, packageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return 0;
+ }
+
+ @Override
+ public void setComponentEnabledSetting(ComponentName componentName,
+ int newState, int flags) {
+ try {
+ mPM.setComponentEnabledSetting(componentName, newState, flags);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public int getComponentEnabledSetting(ComponentName componentName) {
+ try {
+ return mPM.getComponentEnabledSetting(componentName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+
+ @Override
+ public void setApplicationEnabledSetting(String packageName,
+ int newState, int flags) {
+ try {
+ mPM.setApplicationEnabledSetting(packageName, newState, flags);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public int getApplicationEnabledSetting(String packageName) {
+ try {
+ return mPM.getApplicationEnabledSetting(packageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+
+ private final ApplicationContext mContext;
+ private final IPackageManager mPM;
+
+ private static final Object sSync = new Object();
+ private static final Object sPackageRemovedSync = new Object();
+ private static BroadcastReceiver sPackageRemovedReceiver;
+ private static HashMap<ResourceName, WeakReference<Drawable> > sIconCache
+ = new HashMap<ResourceName, WeakReference<Drawable> >();
+ private static HashMap<ResourceName, WeakReference<CharSequence> > sStringCache
+ = new HashMap<ResourceName, WeakReference<CharSequence> >();
+ }
+
+ // ----------------------------------------------------------------------
+ // ----------------------------------------------------------------------
+ // ----------------------------------------------------------------------
+
+ private static final class SharedPreferencesImpl implements SharedPreferences {
+
+ private final File mFile;
+ private final File mBackupFile;
+ private final int mMode;
+ private Map mMap;
+ private final FileStatus mFileStatus = new FileStatus();
+ private long mTimestamp;
+
+ private List<OnSharedPreferenceChangeListener> mListeners;
+
+ SharedPreferencesImpl(
+ File file, int mode, Map initialContents) {
+ mFile = file;
+ mBackupFile = makeBackupFile(file);
+ mMode = mode;
+ mMap = initialContents != null ? initialContents : new HashMap();
+ if (FileUtils.getFileStatus(file.getPath(), mFileStatus)) {
+ mTimestamp = mFileStatus.mtime;
+ }
+ mListeners = new ArrayList<OnSharedPreferenceChangeListener>();
+ }
+
+ public boolean hasFileChanged() {
+ synchronized (this) {
+ if (!FileUtils.getFileStatus(mFile.getPath(), mFileStatus)) {
+ return true;
+ }
+ return mTimestamp != mFileStatus.mtime;
+ }
+ }
+
+ public void replace(Map newContents) {
+ if (newContents != null) {
+ synchronized (this) {
+ mMap = newContents;
+ }
+ }
+ }
+
+ public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+ synchronized(this) {
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ }
+ }
+ }
+
+ public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+ synchronized(this) {
+ mListeners.remove(listener);
+ }
+ }
+
+ public Map<String, ?> getAll() {
+ synchronized(this) {
+ //noinspection unchecked
+ return new HashMap(mMap);
+ }
+ }
+
+ public String getString(String key, String defValue) {
+ synchronized (this) {
+ String v = (String)mMap.get(key);
+ return v != null ? v : defValue;
+ }
+ }
+
+ public int getInt(String key, int defValue) {
+ synchronized (this) {
+ Integer v = (Integer)mMap.get(key);
+ return v != null ? v : defValue;
+ }
+ }
+ public long getLong(String key, long defValue) {
+ synchronized (this) {
+ Long v = (Long) mMap.get(key);
+ return v != null ? v : defValue;
+ }
+ }
+ public float getFloat(String key, float defValue) {
+ synchronized (this) {
+ Float v = (Float)mMap.get(key);
+ return v != null ? v : defValue;
+ }
+ }
+ public boolean getBoolean(String key, boolean defValue) {
+ synchronized (this) {
+ Boolean v = (Boolean)mMap.get(key);
+ return v != null ? v : defValue;
+ }
+ }
+
+ public boolean contains(String key) {
+ synchronized (this) {
+ return mMap.containsKey(key);
+ }
+ }
+
+ public final class EditorImpl implements Editor {
+ private final Map<String, Object> mModified = Maps.newHashMap();
+ private boolean mClear = false;
+
+ public Editor putString(String key, String value) {
+ synchronized (this) {
+ mModified.put(key, value);
+ return this;
+ }
+ }
+ public Editor putInt(String key, int value) {
+ synchronized (this) {
+ mModified.put(key, value);
+ return this;
+ }
+ }
+ public Editor putLong(String key, long value) {
+ synchronized (this) {
+ mModified.put(key, value);
+ return this;
+ }
+ }
+ public Editor putFloat(String key, float value) {
+ synchronized (this) {
+ mModified.put(key, value);
+ return this;
+ }
+ }
+ public Editor putBoolean(String key, boolean value) {
+ synchronized (this) {
+ mModified.put(key, value);
+ return this;
+ }
+ }
+
+ public Editor remove(String key) {
+ synchronized (this) {
+ mModified.put(key, this);
+ return this;
+ }
+ }
+
+ public Editor clear() {
+ synchronized (this) {
+ mClear = true;
+ return this;
+ }
+ }
+
+ public boolean commit() {
+ boolean returnValue;
+
+ boolean hasListeners;
+ List<String> keysModified = null;
+ List<OnSharedPreferenceChangeListener> listeners = null;
+
+ synchronized (SharedPreferencesImpl.this) {
+ hasListeners = mListeners.size() > 0;
+ if (hasListeners) {
+ keysModified = new ArrayList<String>();
+ listeners = new ArrayList<OnSharedPreferenceChangeListener>(mListeners);
+ }
+
+ synchronized (this) {
+ if (mClear) {
+ mMap.clear();
+ mClear = false;
+ }
+
+ Iterator<Entry<String, Object>> it = mModified.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, Object> e = it.next();
+ String k = e.getKey();
+ Object v = e.getValue();
+ if (v == this) {
+ mMap.remove(k);
+ } else {
+ mMap.put(k, v);
+ }
+
+ if (hasListeners) {
+ keysModified.add(k);
+ }
+ }
+
+ mModified.clear();
+ }
+
+ returnValue = writeFileLocked();
+ }
+
+ if (hasListeners) {
+ for (int i = keysModified.size() - 1; i >= 0; i--) {
+ final String key = keysModified.get(i);
+ // Call in the order they were registered
+ final int listenersSize = listeners.size();
+ for (int j = 0; j < listenersSize; j++) {
+ listeners.get(j).onSharedPreferenceChanged(SharedPreferencesImpl.this, key);
+ }
+ }
+ }
+
+ return returnValue;
+ }
+ }
+
+ public Editor edit() {
+ return new EditorImpl();
+ }
+
+ private FileOutputStream createFileOutputStream(File file) {
+ FileOutputStream str = null;
+ try {
+ str = new FileOutputStream(file);
+ } catch (FileNotFoundException e) {
+ File parent = file.getParentFile();
+ if (!parent.mkdir()) {
+ Log.e(TAG, "Couldn't create directory for SharedPreferences file " + file);
+ return null;
+ }
+ FileUtils.setPermissions(
+ parent.getPath(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+ -1, -1);
+ try {
+ str = new FileOutputStream(file);
+ } catch (FileNotFoundException e2) {
+ Log.e(TAG, "Couldn't create SharedPreferences file " + file, e2);
+ }
+ }
+ return str;
+ }
+
+ private boolean writeFileLocked() {
+ // Rename the current file so it may be used as a backup during the next read
+ if (mFile.exists()) {
+ if (!mFile.renameTo(mBackupFile)) {
+ Log.e(TAG, "Couldn't rename file " + mFile + " to backup file " + mBackupFile);
+ }
+ }
+
+ // Attempt to write the file, delete the backup and return true as atomically as
+ // possible. If any exception occurs, delete the new file; next time we will restore
+ // from the backup.
+ try {
+ FileOutputStream str = createFileOutputStream(mFile);
+ if (str == null) {
+ return false;
+ }
+ XmlUtils.writeMapXml(mMap, str);
+ str.close();
+ setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
+ if (FileUtils.getFileStatus(mFile.getPath(), mFileStatus)) {
+ mTimestamp = mFileStatus.mtime;
+ }
+
+ // Writing was successful, delete the backup file
+ if (!mBackupFile.delete()) {
+ Log.e(TAG, "Couldn't delete new backup file " + mBackupFile);
+ }
+ return true;
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "writeFileLocked: Got exception:", e);
+ } catch (IOException e) {
+ Log.w(TAG, "writeFileLocked: Got exception:", e);
+ }
+ // Clean up an unsuccessfully written file
+ if (mFile.exists()) {
+ if (!mFile.delete()) {
+ Log.e(TAG, "Couldn't clean up partially-written file " + mFile);
+ }
+ }
+ return false;
+ }
+ }
+
+ private static class WallpaperCallback extends IWallpaperServiceCallback.Stub {
+ private WeakReference<ApplicationContext> mContext;
+
+ public WallpaperCallback(ApplicationContext context) {
+ mContext = new WeakReference<ApplicationContext>(context);
+ }
+
+ public synchronized void onWallpaperChanged() {
+
+ /* The wallpaper has changed but we shouldn't eagerly load the
+ * wallpaper as that would be inefficient. Reset the cached wallpaper
+ * to null so if the user requests the wallpaper again then we'll
+ * fetch it.
+ */
+ final ApplicationContext applicationContext = mContext.get();
+ if (applicationContext != null) {
+ applicationContext.mWallpaper = null;
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
new file mode 100644
index 0000000..2e301c9
--- /dev/null
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -0,0 +1,72 @@
+/*
+ * 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.app;
+
+import dalvik.system.PathClassLoader;
+
+import java.util.HashMap;
+
+class ApplicationLoaders
+{
+ public static ApplicationLoaders getDefault()
+ {
+ return gApplicationLoaders;
+ }
+
+ public ClassLoader getClassLoader(String zip, String appDataDir,
+ ClassLoader parent)
+ {
+ /*
+ * This is the parent we use if they pass "null" in. In theory
+ * this should be the "system" class loader; in practice we
+ * don't use that and can happily (and more efficiently) use the
+ * bootstrap class loader.
+ */
+ ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();
+
+ synchronized (mLoaders) {
+ if (parent == null) {
+ parent = baseParent;
+ }
+
+ /*
+ * If we're one step up from the base class loader, find
+ * something in our cache. Otherwise, we create a whole
+ * new ClassLoader for the zip archive.
+ */
+ if (parent == baseParent) {
+ ClassLoader loader = (ClassLoader)mLoaders.get(zip);
+ if (loader != null) {
+ return loader;
+ }
+
+ PathClassLoader pathClassloader =
+ new PathClassLoader(zip, appDataDir + "/lib", parent);
+
+ mLoaders.put(zip, pathClassloader);
+ return pathClassloader;
+ }
+
+ return new PathClassLoader(zip, parent);
+ }
+ }
+
+ private final HashMap mLoaders = new HashMap();
+
+ private static final ApplicationLoaders gApplicationLoaders
+ = new ApplicationLoaders();
+}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
new file mode 100644
index 0000000..d2cf55a
--- /dev/null
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -0,0 +1,658 @@
+/*
+ * 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.app;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** {@hide} */
+public abstract class ApplicationThreadNative extends Binder
+ implements IApplicationThread {
+ /**
+ * Cast a Binder object into an application thread interface, generating
+ * a proxy if needed.
+ */
+ static public IApplicationThread asInterface(IBinder obj) {
+ if (obj == null) {
+ return null;
+ }
+ IApplicationThread in =
+ (IApplicationThread)obj.queryLocalInterface(descriptor);
+ if (in != null) {
+ return in;
+ }
+
+ return new ApplicationThreadProxy(obj);
+ }
+
+ public ApplicationThreadNative() {
+ attachInterface(this, descriptor);
+ }
+
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ switch (code) {
+ case SCHEDULE_PAUSE_ACTIVITY_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder b = data.readStrongBinder();
+ boolean finished = data.readInt() != 0;
+ boolean userLeaving = data.readInt() != 0;
+ int configChanges = data.readInt();
+ schedulePauseActivity(b, finished, userLeaving, configChanges);
+ return true;
+ }
+
+ case SCHEDULE_STOP_ACTIVITY_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder b = data.readStrongBinder();
+ boolean show = data.readInt() != 0;
+ int configChanges = data.readInt();
+ scheduleStopActivity(b, show, configChanges);
+ return true;
+ }
+
+ case SCHEDULE_WINDOW_VISIBILITY_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder b = data.readStrongBinder();
+ boolean show = data.readInt() != 0;
+ scheduleWindowVisibility(b, show);
+ return true;
+ }
+
+ case SCHEDULE_RESUME_ACTIVITY_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder b = data.readStrongBinder();
+ boolean isForward = data.readInt() != 0;
+ scheduleResumeActivity(b, isForward);
+ return true;
+ }
+
+ case SCHEDULE_SEND_RESULT_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder b = data.readStrongBinder();
+ List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
+ scheduleSendResult(b, ri);
+ return true;
+ }
+
+ case SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ IBinder b = data.readStrongBinder();
+ ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
+ Bundle state = data.readBundle();
+ List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
+ List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
+ boolean notResumed = data.readInt() != 0;
+ boolean isForward = data.readInt() != 0;
+ scheduleLaunchActivity(intent, b, info, state, ri, pi, notResumed, isForward);
+ return true;
+ }
+
+ case SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder b = data.readStrongBinder();
+ List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
+ List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
+ int configChanges = data.readInt();
+ boolean notResumed = data.readInt() != 0;
+ scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed);
+ return true;
+ }
+
+ case SCHEDULE_NEW_INTENT_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
+ IBinder b = data.readStrongBinder();
+ scheduleNewIntent(pi, b);
+ return true;
+ }
+
+ case SCHEDULE_FINISH_ACTIVITY_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder b = data.readStrongBinder();
+ boolean finishing = data.readInt() != 0;
+ int configChanges = data.readInt();
+ scheduleDestroyActivity(b, finishing, configChanges);
+ return true;
+ }
+
+ case SCHEDULE_RECEIVER_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
+ int resultCode = data.readInt();
+ String resultData = data.readString();
+ Bundle resultExtras = data.readBundle();
+ boolean sync = data.readInt() != 0;
+ scheduleReceiver(intent, info, resultCode, resultData,
+ resultExtras, sync);
+ return true;
+ }
+
+ case SCHEDULE_CREATE_SERVICE_TRANSACTION: {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder token = data.readStrongBinder();
+ ServiceInfo info = ServiceInfo.CREATOR.createFromParcel(data);
+ scheduleCreateService(token, info);
+ return true;
+ }
+
+ case SCHEDULE_BIND_SERVICE_TRANSACTION: {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder token = data.readStrongBinder();
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ boolean rebind = data.readInt() != 0;
+ scheduleBindService(token, intent, rebind);
+ return true;
+ }
+
+ case SCHEDULE_UNBIND_SERVICE_TRANSACTION: {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder token = data.readStrongBinder();
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ scheduleUnbindService(token, intent);
+ return true;
+ }
+
+ case SCHEDULE_SERVICE_ARGS_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder token = data.readStrongBinder();
+ int startId = data.readInt();
+ Intent args = Intent.CREATOR.createFromParcel(data);
+ scheduleServiceArgs(token, startId, args);
+ return true;
+ }
+
+ case SCHEDULE_STOP_SERVICE_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder token = data.readStrongBinder();
+ scheduleStopService(token);
+ return true;
+ }
+
+ case BIND_APPLICATION_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ String packageName = data.readString();
+ ApplicationInfo info =
+ ApplicationInfo.CREATOR.createFromParcel(data);
+ List<ProviderInfo> providers =
+ data.createTypedArrayList(ProviderInfo.CREATOR);
+ ComponentName testName = (data.readInt() != 0)
+ ? new ComponentName(data) : null;
+ String profileName = data.readString();
+ Bundle testArgs = data.readBundle();
+ IBinder binder = data.readStrongBinder();
+ IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder);
+ int testMode = data.readInt();
+ Configuration config = Configuration.CREATOR.createFromParcel(data);
+ HashMap<String, IBinder> services = data.readHashMap(null);
+ bindApplication(packageName, info,
+ providers, testName, profileName,
+ testArgs, testWatcher, testMode, config, services);
+ return true;
+ }
+
+ case SCHEDULE_EXIT_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ scheduleExit();
+ return true;
+ }
+
+ case REQUEST_THUMBNAIL_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder b = data.readStrongBinder();
+ requestThumbnail(b);
+ return true;
+ }
+
+ case SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ Configuration config = Configuration.CREATOR.createFromParcel(data);
+ scheduleConfigurationChanged(config);
+ return true;
+ }
+
+ case UPDATE_TIME_ZONE_TRANSACTION: {
+ data.enforceInterface(IApplicationThread.descriptor);
+ updateTimeZone();
+ return true;
+ }
+
+ case PROCESS_IN_BACKGROUND_TRANSACTION: {
+ data.enforceInterface(IApplicationThread.descriptor);
+ processInBackground();
+ return true;
+ }
+
+ case DUMP_SERVICE_TRANSACTION: {
+ data.enforceInterface(IApplicationThread.descriptor);
+ ParcelFileDescriptor fd = data.readFileDescriptor();
+ final IBinder service = data.readStrongBinder();
+ final String[] args = data.readStringArray();
+ if (fd != null) {
+ dumpService(fd.getFileDescriptor(), service, args);
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ }
+ return true;
+ }
+
+ case SCHEDULE_REGISTERED_RECEIVER_TRANSACTION: {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IIntentReceiver receiver = IIntentReceiver.Stub.asInterface(
+ data.readStrongBinder());
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ int resultCode = data.readInt();
+ String dataStr = data.readString();
+ Bundle extras = data.readBundle();
+ boolean ordered = data.readInt() != 0;
+ scheduleRegisteredReceiver(receiver, intent,
+ resultCode, dataStr, extras, ordered);
+ return true;
+ }
+
+ case SCHEDULE_LOW_MEMORY_TRANSACTION:
+ {
+ scheduleLowMemory();
+ return true;
+ }
+
+ case SCHEDULE_ACTIVITY_CONFIGURATION_CHANGED_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder b = data.readStrongBinder();
+ scheduleActivityConfigurationChanged(b);
+ return true;
+ }
+
+ case REQUEST_PSS_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ requestPss();
+ return true;
+ }
+ }
+
+ return super.onTransact(code, data, reply, flags);
+ }
+
+ public IBinder asBinder()
+ {
+ return this;
+ }
+}
+
+class ApplicationThreadProxy implements IApplicationThread {
+ private final IBinder mRemote;
+
+ public ApplicationThreadProxy(IBinder remote) {
+ mRemote = remote;
+ }
+
+ public final IBinder asBinder() {
+ return mRemote;
+ }
+
+ public final void schedulePauseActivity(IBinder token, boolean finished,
+ boolean userLeaving, int configChanges) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(finished ? 1 : 0);
+ data.writeInt(userLeaving ? 1 :0);
+ data.writeInt(configChanges);
+ mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleStopActivity(IBinder token, boolean showWindow,
+ int configChanges) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(showWindow ? 1 : 0);
+ data.writeInt(configChanges);
+ mRemote.transact(SCHEDULE_STOP_ACTIVITY_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleWindowVisibility(IBinder token,
+ boolean showWindow) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(showWindow ? 1 : 0);
+ mRemote.transact(SCHEDULE_WINDOW_VISIBILITY_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleResumeActivity(IBinder token, boolean isForward)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(isForward ? 1 : 0);
+ mRemote.transact(SCHEDULE_RESUME_ACTIVITY_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleSendResult(IBinder token, List<ResultInfo> results)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeTypedList(results);
+ mRemote.transact(SCHEDULE_SEND_RESULT_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleLaunchActivity(Intent intent, IBinder token,
+ ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
+ List<Intent> pendingNewIntents, boolean notResumed, boolean isForward)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ intent.writeToParcel(data, 0);
+ data.writeStrongBinder(token);
+ info.writeToParcel(data, 0);
+ data.writeBundle(state);
+ data.writeTypedList(pendingResults);
+ data.writeTypedList(pendingNewIntents);
+ data.writeInt(notResumed ? 1 : 0);
+ data.writeInt(isForward ? 1 : 0);
+ mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleRelaunchActivity(IBinder token,
+ List<ResultInfo> pendingResults, List<Intent> pendingNewIntents,
+ int configChanges, boolean notResumed) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeTypedList(pendingResults);
+ data.writeTypedList(pendingNewIntents);
+ data.writeInt(configChanges);
+ data.writeInt(notResumed ? 1 : 0);
+ mRemote.transact(SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public void scheduleNewIntent(List<Intent> intents, IBinder token)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeTypedList(intents);
+ data.writeStrongBinder(token);
+ mRemote.transact(SCHEDULE_NEW_INTENT_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleDestroyActivity(IBinder token, boolean finishing,
+ int configChanges) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(finishing ? 1 : 0);
+ data.writeInt(configChanges);
+ mRemote.transact(SCHEDULE_FINISH_ACTIVITY_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleReceiver(Intent intent, ActivityInfo info,
+ int resultCode, String resultData,
+ Bundle map, boolean sync) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ intent.writeToParcel(data, 0);
+ info.writeToParcel(data, 0);
+ data.writeInt(resultCode);
+ data.writeString(resultData);
+ data.writeBundle(map);
+ data.writeInt(sync ? 1 : 0);
+ mRemote.transact(SCHEDULE_RECEIVER_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleCreateService(IBinder token, ServiceInfo info)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ info.writeToParcel(data, 0);
+ mRemote.transact(SCHEDULE_CREATE_SERVICE_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleBindService(IBinder token, Intent intent, boolean rebind)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ intent.writeToParcel(data, 0);
+ data.writeInt(rebind ? 1 : 0);
+ mRemote.transact(SCHEDULE_BIND_SERVICE_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleUnbindService(IBinder token, Intent intent)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ intent.writeToParcel(data, 0);
+ mRemote.transact(SCHEDULE_UNBIND_SERVICE_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleServiceArgs(IBinder token, int startId,
+ Intent args) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(startId);
+ args.writeToParcel(data, 0);
+ mRemote.transact(SCHEDULE_SERVICE_ARGS_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleStopService(IBinder token)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(SCHEDULE_STOP_SERVICE_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void bindApplication(String packageName, ApplicationInfo info,
+ List<ProviderInfo> providers, ComponentName testName,
+ String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode,
+ Configuration config, Map<String, IBinder> services) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeString(packageName);
+ info.writeToParcel(data, 0);
+ data.writeTypedList(providers);
+ if (testName == null) {
+ data.writeInt(0);
+ } else {
+ data.writeInt(1);
+ testName.writeToParcel(data, 0);
+ }
+ data.writeString(profileName);
+ data.writeBundle(testArgs);
+ data.writeStrongInterface(testWatcher);
+ data.writeInt(debugMode);
+ config.writeToParcel(data, 0);
+ data.writeMap(services);
+ mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleExit() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ mRemote.transact(SCHEDULE_EXIT_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void requestThumbnail(IBinder token)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(REQUEST_THUMBNAIL_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleConfigurationChanged(Configuration config)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ config.writeToParcel(data, 0);
+ mRemote.transact(SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public void updateTimeZone() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ mRemote.transact(UPDATE_TIME_ZONE_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public void processInBackground() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ mRemote.transact(PROCESS_IN_BACKGROUND_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public void dumpService(FileDescriptor fd, IBinder token, String[] args)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeFileDescriptor(fd);
+ data.writeStrongBinder(token);
+ data.writeStringArray(args);
+ mRemote.transact(DUMP_SERVICE_TRANSACTION, data, null, 0);
+ data.recycle();
+ }
+
+ public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
+ int resultCode, String dataStr, Bundle extras, boolean ordered)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(receiver.asBinder());
+ intent.writeToParcel(data, 0);
+ data.writeInt(resultCode);
+ data.writeString(dataStr);
+ data.writeBundle(extras);
+ data.writeInt(ordered ? 1 : 0);
+ mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleLowMemory() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ mRemote.transact(SCHEDULE_LOW_MEMORY_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void scheduleActivityConfigurationChanged(
+ IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(SCHEDULE_ACTIVITY_CONFIGURATION_CHANGED_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ public final void requestPss() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ mRemote.transact(REQUEST_PSS_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+}
+
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
new file mode 100644
index 0000000..ee5e0d5
--- /dev/null
+++ b/core/java/android/app/DatePickerDialog.java
@@ -0,0 +1,185 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+import android.text.TextUtils.TruncateAt;
+import android.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.DatePicker;
+import android.widget.TextView;
+import android.widget.DatePicker.OnDateChangedListener;
+
+import com.android.internal.R;
+
+import java.text.DateFormatSymbols;
+import java.util.Calendar;
+
+/**
+ * A simple dialog containing an {@link android.widget.DatePicker}.
+ */
+public class DatePickerDialog extends AlertDialog implements OnClickListener,
+ OnDateChangedListener {
+
+ private static final String YEAR = "year";
+ private static final String MONTH = "month";
+ private static final String DAY = "day";
+
+ private final DatePicker mDatePicker;
+ private final OnDateSetListener mCallBack;
+ private final Calendar mCalendar;
+ private final java.text.DateFormat mDateFormat;
+ private final String[] mWeekDays;
+
+ private int mInitialYear;
+ private int mInitialMonth;
+ private int mInitialDay;
+
+ /**
+ * The callback used to indicate the user is done filling in the date.
+ */
+ public interface OnDateSetListener {
+
+ /**
+ * @param view The view associated with this listener.
+ * @param year The year that was set.
+ * @param monthOfYear The month that was set (0-11) for compatibility
+ * with {@link java.util.Calendar}.
+ * @param dayOfMonth The day of the month that was set.
+ */
+ void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth);
+ }
+
+ /**
+ * @param context The context the dialog is to run in.
+ * @param callBack How the parent is notified that the date is set.
+ * @param year The initial year of the dialog.
+ * @param monthOfYear The initial month of the dialog.
+ * @param dayOfMonth The initial day of the dialog.
+ */
+ public DatePickerDialog(Context context,
+ OnDateSetListener callBack,
+ int year,
+ int monthOfYear,
+ int dayOfMonth) {
+ this(context, com.android.internal.R.style.Theme_Dialog_Alert,
+ callBack, year, monthOfYear, dayOfMonth);
+ }
+
+ /**
+ * @param context The context the dialog is to run in.
+ * @param theme the theme to apply to this dialog
+ * @param callBack How the parent is notified that the date is set.
+ * @param year The initial year of the dialog.
+ * @param monthOfYear The initial month of the dialog.
+ * @param dayOfMonth The initial day of the dialog.
+ */
+ public DatePickerDialog(Context context,
+ int theme,
+ OnDateSetListener callBack,
+ int year,
+ int monthOfYear,
+ int dayOfMonth) {
+ super(context, theme);
+
+ mCallBack = callBack;
+ mInitialYear = year;
+ mInitialMonth = monthOfYear;
+ mInitialDay = dayOfMonth;
+ DateFormatSymbols symbols = new DateFormatSymbols();
+ mWeekDays = symbols.getShortWeekdays();
+
+ mDateFormat = DateFormat.getMediumDateFormat(context);
+ mCalendar = Calendar.getInstance();
+ updateTitle(mInitialYear, mInitialMonth, mInitialDay);
+
+ setButton(context.getText(R.string.date_time_set), this);
+ setButton2(context.getText(R.string.cancel), (OnClickListener) null);
+ setIcon(R.drawable.ic_dialog_time);
+
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(R.layout.date_picker_dialog, null);
+ setView(view);
+ mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
+ mDatePicker.init(mInitialYear, mInitialMonth, mInitialDay, this);
+ }
+
+ @Override
+ public void show() {
+ super.show();
+
+ /* Sometimes the full month is displayed causing the title
+ * to be very long, in those cases ensure it doesn't wrap to
+ * 2 lines (as that looks jumpy) and ensure we ellipsize the end.
+ */
+ TextView title = (TextView) findViewById(R.id.alertTitle);
+ title.setSingleLine();
+ title.setEllipsize(TruncateAt.END);
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (mCallBack != null) {
+ mDatePicker.clearFocus();
+ mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
+ mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
+ }
+ }
+
+ public void onDateChanged(DatePicker view, int year,
+ int month, int day) {
+ updateTitle(year, month, day);
+ }
+
+ public void updateDate(int year, int monthOfYear, int dayOfMonth) {
+ mInitialYear = year;
+ mInitialMonth = monthOfYear;
+ mInitialDay = dayOfMonth;
+ mDatePicker.updateDate(year, monthOfYear, dayOfMonth);
+ }
+
+ private void updateTitle(int year, int month, int day) {
+ mCalendar.set(Calendar.YEAR, year);
+ mCalendar.set(Calendar.MONTH, month);
+ mCalendar.set(Calendar.DAY_OF_MONTH, day);
+ String weekday = mWeekDays[mCalendar.get(Calendar.DAY_OF_WEEK)];
+ setTitle(weekday + ", " + mDateFormat.format(mCalendar.getTime()));
+ }
+
+ @Override
+ public Bundle onSaveInstanceState() {
+ Bundle state = super.onSaveInstanceState();
+ state.putInt(YEAR, mDatePicker.getYear());
+ state.putInt(MONTH, mDatePicker.getMonth());
+ state.putInt(DAY, mDatePicker.getDayOfMonth());
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ int year = savedInstanceState.getInt(YEAR);
+ int month = savedInstanceState.getInt(MONTH);
+ int day = savedInstanceState.getInt(DAY);
+ mDatePicker.init(year, month, day, this);
+ updateTitle(year, month, day);
+ }
+}
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
new file mode 100644
index 0000000..b09a57f
--- /dev/null
+++ b/core/java/android/app/Dialog.java
@@ -0,0 +1,954 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Bundle;
+import android.util.Config;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.LayoutInflater;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnCreateContextMenuListener;
+
+import com.android.internal.policy.PolicyManager;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Base class for Dialogs.
+ *
+ * <p>Note: Activities provide a facility to manage the creation, saving and
+ * restoring of dialogs. See {@link Activity#onCreateDialog(int)},
+ * {@link Activity#onPrepareDialog(int, Dialog)},
+ * {@link Activity#showDialog(int)}, and {@link Activity#dismissDialog(int)}. If
+ * these methods are used, {@link #getOwnerActivity()} will return the Activity
+ * that managed this dialog.
+ *
+ * <p>Often you will want to have a Dialog display on top of the current
+ * input method, because there is no reason for it to accept text. You can
+ * do this by setting the {@link WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
+ * WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} window flag (assuming
+ * your Dialog takes input focus, as it the default) with the following code:
+ *
+ * <pre>
+ * getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ * WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ * </pre>
+ */
+public class Dialog implements DialogInterface, Window.Callback,
+ KeyEvent.Callback, OnCreateContextMenuListener {
+ private static final String LOG_TAG = "Dialog";
+
+ private Activity mOwnerActivity;
+
+ final Context mContext;
+ final WindowManager mWindowManager;
+ Window mWindow;
+ View mDecor;
+ /**
+ * This field should be made private, so it is hidden from the SDK.
+ * {@hide}
+ */
+ protected boolean mCancelable = true;
+ private Message mCancelMessage;
+ private Message mDismissMessage;
+
+ /**
+ * Whether to cancel the dialog when a touch is received outside of the
+ * window's bounds.
+ */
+ private boolean mCanceledOnTouchOutside = false;
+
+ private OnKeyListener mOnKeyListener;
+
+ private boolean mCreated = false;
+ private boolean mShowing = false;
+
+ private final Thread mUiThread;
+ private final Handler mHandler = new Handler();
+
+ private final Runnable mDismissAction = new Runnable() {
+ public void run() {
+ dismissDialog();
+ }
+ };
+
+ /**
+ * Create a Dialog window that uses the default dialog frame style.
+ *
+ * @param context The Context the Dialog is to run it. In particular, it
+ * uses the window manager and theme in this context to
+ * present its UI.
+ */
+ public Dialog(Context context) {
+ this(context, 0);
+ }
+
+ /**
+ * Create a Dialog window that uses a custom dialog style.
+ *
+ * @param context The Context in which the Dialog should run. In particular, it
+ * uses the window manager and theme from this context to
+ * present its UI.
+ * @param theme A style resource describing the theme to use for the
+ * window. See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style
+ * and Theme Resources</a> for more information about defining and using
+ * styles. This theme is applied on top of the current theme in
+ * <var>context</var>. If 0, the default dialog theme will be used.
+ */
+ public Dialog(Context context, int theme) {
+ mContext = new ContextThemeWrapper(
+ context, theme == 0 ? com.android.internal.R.style.Theme_Dialog : theme);
+ mWindowManager = (WindowManager)context.getSystemService("window");
+ Window w = PolicyManager.makeNewWindow(mContext);
+ mWindow = w;
+ w.setCallback(this);
+ w.setWindowManager(mWindowManager, null, null);
+ w.setGravity(Gravity.CENTER);
+ mUiThread = Thread.currentThread();
+ mDismissCancelHandler = new DismissCancelHandler(this);
+ }
+
+ /**
+ * @deprecated
+ * @hide
+ */
+ @Deprecated
+ protected Dialog(Context context, boolean cancelable,
+ Message cancelCallback) {
+ this(context);
+ mCancelable = cancelable;
+ mCancelMessage = cancelCallback;
+ }
+
+ protected Dialog(Context context, boolean cancelable,
+ OnCancelListener cancelListener) {
+ this(context);
+ mCancelable = cancelable;
+ setOnCancelListener(cancelListener);
+ }
+
+ /**
+ * Retrieve the Context this Dialog is running in.
+ *
+ * @return Context The Context that was supplied to the constructor.
+ */
+ public final Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Sets the Activity that owns this dialog. An example use: This Dialog will
+ * use the suggested volume control stream of the Activity.
+ *
+ * @param activity The Activity that owns this dialog.
+ */
+ public final void setOwnerActivity(Activity activity) {
+ mOwnerActivity = activity;
+
+ getWindow().setVolumeControlStream(mOwnerActivity.getVolumeControlStream());
+ }
+
+ /**
+ * Returns the Activity that owns this Dialog. For example, if
+ * {@link Activity#showDialog(int)} is used to show this Dialog, that
+ * Activity will be the owner (by default). Depending on how this dialog was
+ * created, this may return null.
+ *
+ * @return The Activity that owns this Dialog.
+ */
+ public final Activity getOwnerActivity() {
+ return mOwnerActivity;
+ }
+
+ /**
+ * @return Whether the dialog is currently showing.
+ */
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ /**
+ * Start the dialog and display it on screen. The window is placed in the
+ * application layer and opaque. Note that you should not override this
+ * method to do initialization when the dialog is shown, instead implement
+ * that in {@link #onStart}.
+ */
+ public void show() {
+ if (mShowing) {
+ if (Config.LOGV) Log.v(LOG_TAG,
+ "[Dialog] start: already showing, ignore");
+ if (mDecor != null) mDecor.setVisibility(View.VISIBLE);
+ return;
+ }
+
+ if (!mCreated) {
+ dispatchOnCreate(null);
+ }
+
+ onStart();
+ mDecor = mWindow.getDecorView();
+ WindowManager.LayoutParams l = mWindow.getAttributes();
+ if ((l.softInputMode
+ & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
+ WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
+ nl.copyFrom(l);
+ nl.softInputMode |=
+ WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+ l = nl;
+ }
+ mWindowManager.addView(mDecor, l);
+ mShowing = true;
+ }
+
+ /**
+ * Hide the dialog, but do not dismiss it.
+ */
+ public void hide() {
+ if (mDecor != null) mDecor.setVisibility(View.GONE);
+ }
+
+ /**
+ * Dismiss this dialog, removing it from the screen. This method can be
+ * invoked safely from any thread. Note that you should not override this
+ * method to do cleanup when the dialog is dismissed, instead implement
+ * that in {@link #onStop}.
+ */
+ public void dismiss() {
+ if (Thread.currentThread() != mUiThread) {
+ mHandler.post(mDismissAction);
+ } else {
+ mDismissAction.run();
+ }
+ }
+
+ private void dismissDialog() {
+ if (mDecor == null) {
+ if (Config.LOGV) Log.v(LOG_TAG,
+ "[Dialog] dismiss: already dismissed, ignore");
+ return;
+ }
+ if (!mShowing) {
+ if (Config.LOGV) Log.v(LOG_TAG,
+ "[Dialog] dismiss: not showing, ignore");
+ return;
+ }
+
+ mWindowManager.removeView(mDecor);
+ mDecor = null;
+ mWindow.closeAllPanels();
+ onStop();
+ mShowing = false;
+
+ sendDismissMessage();
+ }
+
+ private void sendDismissMessage() {
+ if (mDismissMessage != null) {
+ // Obtain a new message so this dialog can be re-used
+ Message.obtain(mDismissMessage).sendToTarget();
+ }
+ }
+
+ // internal method to make sure mcreated is set properly without requiring
+ // users to call through to super in onCreate
+ void dispatchOnCreate(Bundle savedInstanceState) {
+ onCreate(savedInstanceState);
+ mCreated = true;
+ }
+
+ /**
+ * Similar to {@link Activity#onCreate}, you should initialized your dialog
+ * in this method, including calling {@link #setContentView}.
+ * @param savedInstanceState If this dialog is being reinitalized after a
+ * the hosting activity was previously shut down, holds the result from
+ * the most recent call to {@link #onSaveInstanceState}, or null if this
+ * is the first time.
+ */
+ protected void onCreate(Bundle savedInstanceState) {
+ }
+
+ /**
+ * Called when the dialog is starting.
+ */
+ protected void onStart() {
+ }
+
+ /**
+ * Called to tell you that you're stopping.
+ */
+ protected void onStop() {
+ }
+
+ private static final String DIALOG_SHOWING_TAG = "android:dialogShowing";
+ private static final String DIALOG_HIERARCHY_TAG = "android:dialogHierarchy";
+
+ /**
+ * Saves the state of the dialog into a bundle.
+ *
+ * The default implementation saves the state of its view hierarchy, so you'll
+ * likely want to call through to super if you override this to save additional
+ * state.
+ * @return A bundle with the state of the dialog.
+ */
+ public Bundle onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(DIALOG_SHOWING_TAG, mShowing);
+ if (mCreated) {
+ bundle.putBundle(DIALOG_HIERARCHY_TAG, mWindow.saveHierarchyState());
+ }
+ return bundle;
+ }
+
+ /**
+ * Restore the state of the dialog from a previously saved bundle.
+ *
+ * The default implementation restores the state of the dialog's view
+ * hierarchy that was saved in the default implementation of {@link #onSaveInstanceState()},
+ * so be sure to call through to super when overriding unless you want to
+ * do all restoring of state yourself.
+ * @param savedInstanceState The state of the dialog previously saved by
+ * {@link #onSaveInstanceState()}.
+ */
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG);
+ if (dialogHierarchyState == null) {
+ // dialog has never been shown, or onCreated, nothing to restore.
+ return;
+ }
+ dispatchOnCreate(savedInstanceState);
+ mWindow.restoreHierarchyState(dialogHierarchyState);
+ if (savedInstanceState.getBoolean(DIALOG_SHOWING_TAG)) {
+ show();
+ }
+ }
+
+ /**
+ * Retrieve the current Window for the activity. This can be used to
+ * directly access parts of the Window API that are not available
+ * through Activity/Screen.
+ *
+ * @return Window The current window, or null if the activity is not
+ * visual.
+ */
+ public Window getWindow() {
+ return mWindow;
+ }
+
+ /**
+ * Call {@link android.view.Window#getCurrentFocus} on the
+ * Window if this Activity to return the currently focused view.
+ *
+ * @return View The current View with focus or null.
+ *
+ * @see #getWindow
+ * @see android.view.Window#getCurrentFocus
+ */
+ public View getCurrentFocus() {
+ return mWindow != null ? mWindow.getCurrentFocus() : null;
+ }
+
+ /**
+ * Finds a view that was identified by the id attribute from the XML that
+ * was processed in {@link #onStart}.
+ *
+ * @param id the identifier of the view to find
+ * @return The view if found or null otherwise.
+ */
+ public View findViewById(int id) {
+ return mWindow.findViewById(id);
+ }
+
+ /**
+ * Set the screen content from a layout resource. The resource will be
+ * inflated, adding all top-level views to the screen.
+ *
+ * @param layoutResID Resource ID to be inflated.
+ */
+ public void setContentView(int layoutResID) {
+ mWindow.setContentView(layoutResID);
+ }
+
+ /**
+ * Set the screen content to an explicit view. This view is placed
+ * directly into the screen's view hierarchy. It can itself be a complex
+ * view hierarhcy.
+ *
+ * @param view The desired content to display.
+ */
+ public void setContentView(View view) {
+ mWindow.setContentView(view);
+ }
+
+ /**
+ * Set the screen content to an explicit view. This view is placed
+ * directly into the screen's view hierarchy. It can itself be a complex
+ * view hierarhcy.
+ *
+ * @param view The desired content to display.
+ * @param params Layout parameters for the view.
+ */
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ mWindow.setContentView(view, params);
+ }
+
+ /**
+ * Add an additional content view to the screen. Added after any existing
+ * ones in the screen -- existing views are NOT removed.
+ *
+ * @param view The desired content to display.
+ * @param params Layout parameters for the view.
+ */
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ mWindow.addContentView(view, params);
+ }
+
+ /**
+ * Set the title text for this dialog's window.
+ *
+ * @param title The new text to display in the title.
+ */
+ public void setTitle(CharSequence title) {
+ mWindow.setTitle(title);
+ mWindow.getAttributes().setTitle(title);
+ }
+
+ /**
+ * Set the title text for this dialog's window. The text is retrieved
+ * from the resources with the supplied identifier.
+ *
+ * @param titleId the title's text resource identifier
+ */
+ public void setTitle(int titleId) {
+ setTitle(mContext.getText(titleId));
+ }
+
+ /**
+ * A key was pressed down.
+ *
+ * <p>If the focused view didn't want this event, this method is called.
+ *
+ * <p>The default implementation handles KEYCODE_BACK to close the
+ * dialog.
+ *
+ * @see #onKeyUp
+ * @see android.view.KeyEvent
+ */
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (mCancelable) {
+ cancel();
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * A key was released.
+ *
+ * @see #onKeyDown
+ * @see KeyEvent
+ */
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
+ * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle
+ * the event).
+ */
+ public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Called when a touch screen event was not handled by any of the views
+ * under it. This is most useful to process touch events that happen outside
+ * of your window bounds, where there is no view to receive it.
+ *
+ * @param event The touch screen event being processed.
+ * @return Return true if you have consumed the event, false if you haven't.
+ * The default implementation will cancel the dialog when a touch
+ * happens outside of the window bounds.
+ */
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mCancelable && mCanceledOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
+ && isOutOfBounds(event)) {
+ cancel();
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean isOutOfBounds(MotionEvent event) {
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
+ final View decorView = getWindow().getDecorView();
+ return (x < -slop) || (y < -slop)
+ || (x > (decorView.getWidth()+slop))
+ || (y > (decorView.getHeight()+slop));
+ }
+
+ /**
+ * Called when the trackball was moved and not handled by any of the
+ * views inside of the activity. So, for example, if the trackball moves
+ * while focus is on a button, you will receive a call here because
+ * buttons do not normally do anything with trackball events. The call
+ * here happens <em>before</em> trackball movements are converted to
+ * DPAD key events, which then get sent back to the view hierarchy, and
+ * will be processed at the point for things like focus navigation.
+ *
+ * @param event The trackball event being processed.
+ *
+ * @return Return true if you have consumed the event, false if you haven't.
+ * The default implementation always returns false.
+ */
+ public boolean onTrackballEvent(MotionEvent event) {
+ return false;
+ }
+
+ public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
+ if (mDecor != null) {
+ mWindowManager.updateViewLayout(mDecor, params);
+ }
+ }
+
+ public void onContentChanged() {
+ }
+
+ public void onWindowFocusChanged(boolean hasFocus) {
+ }
+
+ /**
+ * Called to process key events. You can override this to intercept all
+ * key events before they are dispatched to the window. Be sure to call
+ * this implementation for key events that should be handled normally.
+ *
+ * @param event The key event.
+ *
+ * @return boolean Return true if this event was consumed.
+ */
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if ((mOnKeyListener != null) && (mOnKeyListener.onKey(this, event.getKeyCode(), event))) {
+ return true;
+ }
+ if (mWindow.superDispatchKeyEvent(event)) {
+ return true;
+ }
+ return event.dispatch(this);
+ }
+
+ /**
+ * Called to process touch screen events. You can override this to
+ * intercept all touch screen events before they are dispatched to the
+ * window. Be sure to call this implementation for touch screen events
+ * that should be handled normally.
+ *
+ * @param ev The touch screen event.
+ *
+ * @return boolean Return true if this event was consumed.
+ */
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (mWindow.superDispatchTouchEvent(ev)) {
+ return true;
+ }
+ return onTouchEvent(ev);
+ }
+
+ /**
+ * Called to process trackball events. You can override this to
+ * intercept all trackball events before they are dispatched to the
+ * window. Be sure to call this implementation for trackball events
+ * that should be handled normally.
+ *
+ * @param ev The trackball event.
+ *
+ * @return boolean Return true if this event was consumed.
+ */
+ public boolean dispatchTrackballEvent(MotionEvent ev) {
+ if (mWindow.superDispatchTrackballEvent(ev)) {
+ return true;
+ }
+ return onTrackballEvent(ev);
+ }
+
+ /**
+ * @see Activity#onCreatePanelView(int)
+ */
+ public View onCreatePanelView(int featureId) {
+ return null;
+ }
+
+ /**
+ * @see Activity#onCreatePanelMenu(int, Menu)
+ */
+ public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onCreateOptionsMenu(menu);
+ }
+
+ return false;
+ }
+
+ /**
+ * @see Activity#onPreparePanel(int, View, Menu)
+ */
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
+ boolean goforit = onPrepareOptionsMenu(menu);
+ return goforit && menu.hasVisibleItems();
+ }
+ return true;
+ }
+
+ /**
+ * @see Activity#onMenuOpened(int, Menu)
+ */
+ public boolean onMenuOpened(int featureId, Menu menu) {
+ return true;
+ }
+
+ /**
+ * @see Activity#onMenuItemSelected(int, MenuItem)
+ */
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ return false;
+ }
+
+ /**
+ * @see Activity#onPanelClosed(int, Menu)
+ */
+ public void onPanelClosed(int featureId, Menu menu) {
+ }
+
+ /**
+ * It is usually safe to proxy this call to the owner activity's
+ * {@link Activity#onCreateOptionsMenu(Menu)} if the client desires the same
+ * menu for this Dialog.
+ *
+ * @see Activity#onCreateOptionsMenu(Menu)
+ * @see #getOwnerActivity()
+ */
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ /**
+ * It is usually safe to proxy this call to the owner activity's
+ * {@link Activity#onPrepareOptionsMenu(Menu)} if the client desires the
+ * same menu for this Dialog.
+ *
+ * @see Activity#onPrepareOptionsMenu(Menu)
+ * @see #getOwnerActivity()
+ */
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ /**
+ * @see Activity#onOptionsItemSelected(MenuItem)
+ */
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return false;
+ }
+
+ /**
+ * @see Activity#onOptionsMenuClosed(Menu)
+ */
+ public void onOptionsMenuClosed(Menu menu) {
+ }
+
+ /**
+ * @see Activity#openOptionsMenu()
+ */
+ public void openOptionsMenu() {
+ mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
+ }
+
+ /**
+ * @see Activity#closeOptionsMenu()
+ */
+ public void closeOptionsMenu() {
+ mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+ }
+
+ /**
+ * @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)
+ */
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ }
+
+ /**
+ * @see Activity#registerForContextMenu(View)
+ */
+ public void registerForContextMenu(View view) {
+ view.setOnCreateContextMenuListener(this);
+ }
+
+ /**
+ * @see Activity#unregisterForContextMenu(View)
+ */
+ public void unregisterForContextMenu(View view) {
+ view.setOnCreateContextMenuListener(null);
+ }
+
+ /**
+ * @see Activity#openContextMenu(View)
+ */
+ public void openContextMenu(View view) {
+ view.showContextMenu();
+ }
+
+ /**
+ * @see Activity#onContextItemSelected(MenuItem)
+ */
+ public boolean onContextItemSelected(MenuItem item) {
+ return false;
+ }
+
+ /**
+ * @see Activity#onContextMenuClosed(Menu)
+ */
+ public void onContextMenuClosed(Menu menu) {
+ }
+
+ /**
+ * This hook is called when the user signals the desire to start a search.
+ */
+ public boolean onSearchRequested() {
+ // not during dialogs, no.
+ return false;
+ }
+
+
+ /**
+ * Request that key events come to this dialog. Use this if your
+ * dialog has no views with focus, but the dialog still wants
+ * a chance to process key events.
+ *
+ * @param get true if the dialog should receive key events, false otherwise
+ * @see android.view.Window#takeKeyEvents
+ */
+ public void takeKeyEvents(boolean get) {
+ mWindow.takeKeyEvents(get);
+ }
+
+ /**
+ * Enable extended window features. This is a convenience for calling
+ * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
+ *
+ * @param featureId The desired feature as defined in
+ * {@link android.view.Window}.
+ * @return Returns true if the requested feature is supported and now
+ * enabled.
+ *
+ * @see android.view.Window#requestFeature
+ */
+ public final boolean requestWindowFeature(int featureId) {
+ return getWindow().requestFeature(featureId);
+ }
+
+ /**
+ * Convenience for calling
+ * {@link android.view.Window#setFeatureDrawableResource}.
+ */
+ public final void setFeatureDrawableResource(int featureId, int resId) {
+ getWindow().setFeatureDrawableResource(featureId, resId);
+ }
+
+ /**
+ * Convenience for calling
+ * {@link android.view.Window#setFeatureDrawableUri}.
+ */
+ public final void setFeatureDrawableUri(int featureId, Uri uri) {
+ getWindow().setFeatureDrawableUri(featureId, uri);
+ }
+
+ /**
+ * Convenience for calling
+ * {@link android.view.Window#setFeatureDrawable(int, Drawable)}.
+ */
+ public final void setFeatureDrawable(int featureId, Drawable drawable) {
+ getWindow().setFeatureDrawable(featureId, drawable);
+ }
+
+ /**
+ * Convenience for calling
+ * {@link android.view.Window#setFeatureDrawableAlpha}.
+ */
+ public final void setFeatureDrawableAlpha(int featureId, int alpha) {
+ getWindow().setFeatureDrawableAlpha(featureId, alpha);
+ }
+
+ public LayoutInflater getLayoutInflater() {
+ return getWindow().getLayoutInflater();
+ }
+
+ /**
+ * Sets whether this dialog is cancelable with the
+ * {@link KeyEvent#KEYCODE_BACK BACK} key.
+ */
+ public void setCancelable(boolean flag) {
+ mCancelable = flag;
+ }
+
+ /**
+ * Sets whether this dialog is canceled when touched outside the window's
+ * bounds. If setting to true, the dialog is set to be cancelable if not
+ * already set.
+ *
+ * @param cancel Whether the dialog should be canceled when touched outside
+ * the window.
+ */
+ public void setCanceledOnTouchOutside(boolean cancel) {
+ if (cancel && !mCancelable) {
+ mCancelable = true;
+ }
+
+ mCanceledOnTouchOutside = cancel;
+ }
+
+ /**
+ * Cancel the dialog. This is essentially the same as calling {@link #dismiss()}, but it will
+ * also call your {@link DialogInterface.OnCancelListener} (if registered).
+ */
+ public void cancel() {
+ if (mCancelMessage != null) {
+
+ // Obtain a new message so this dialog can be re-used
+ Message.obtain(mCancelMessage).sendToTarget();
+ }
+ dismiss();
+ }
+
+ /**
+ * Set a listener to be invoked when the dialog is canceled.
+ * <p>
+ * This will only be invoked when the dialog is canceled, if the creator
+ * needs to know when it is dismissed in general, use
+ * {@link #setOnDismissListener}.
+ *
+ * @param listener The {@link DialogInterface.OnCancelListener} to use.
+ */
+ public void setOnCancelListener(final OnCancelListener listener) {
+ if (listener != null) {
+ mCancelMessage = mDismissCancelHandler.obtainMessage(CANCEL, listener);
+ } else {
+ mCancelMessage = null;
+ }
+ }
+
+ /**
+ * Set a message to be sent when the dialog is canceled.
+ * @param msg The msg to send when the dialog is canceled.
+ * @see #setOnCancelListener(android.content.DialogInterface.OnCancelListener)
+ */
+ public void setCancelMessage(final Message msg) {
+ mCancelMessage = msg;
+ }
+
+ /**
+ * Set a listener to be invoked when the dialog is dismissed.
+ * @param listener The {@link DialogInterface.OnDismissListener} to use.
+ */
+ public void setOnDismissListener(final OnDismissListener listener) {
+ if (listener != null) {
+ mDismissMessage = mDismissCancelHandler.obtainMessage(DISMISS, listener);
+ } else {
+ mDismissMessage = null;
+ }
+ }
+
+ /**
+ * Set a message to be sent when the dialog is dismissed.
+ * @param msg The msg to send when the dialog is dismissed.
+ */
+ public void setDismissMessage(final Message msg) {
+ mDismissMessage = msg;
+ }
+
+ /**
+ * By default, this will use the owner Activity's suggested stream type.
+ *
+ * @see Activity#setVolumeControlStream(int)
+ * @see #setOwnerActivity(Activity)
+ */
+ public final void setVolumeControlStream(int streamType) {
+ getWindow().setVolumeControlStream(streamType);
+ }
+
+ /**
+ * @see Activity#getVolumeControlStream()
+ */
+ public final int getVolumeControlStream() {
+ return getWindow().getVolumeControlStream();
+ }
+
+ /**
+ * Sets the callback that will be called if a key is dispatched to the dialog.
+ */
+ public void setOnKeyListener(final OnKeyListener onKeyListener) {
+ mOnKeyListener = onKeyListener;
+ }
+
+ private static final int DISMISS = 0x43;
+ private static final int CANCEL = 0x44;
+
+ private Handler mDismissCancelHandler;
+
+ private static final class DismissCancelHandler extends Handler {
+ private WeakReference<DialogInterface> mDialog;
+
+ public DismissCancelHandler(Dialog dialog) {
+ mDialog = new WeakReference<DialogInterface>(dialog);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case DISMISS:
+ ((OnDismissListener) msg.obj).onDismiss(mDialog.get());
+ break;
+ case CANCEL:
+ ((OnCancelListener) msg.obj).onCancel(mDialog.get());
+ break;
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/ExpandableListActivity.java b/core/java/android/app/ExpandableListActivity.java
new file mode 100644
index 0000000..a2e048f
--- /dev/null
+++ b/core/java/android/app/ExpandableListActivity.java
@@ -0,0 +1,324 @@
+/*
+ * 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.app;
+
+import android.database.Cursor;
+import android.os.Bundle;
+import java.util.List;
+import android.view.ContextMenu;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnCreateContextMenuListener;
+import android.widget.ExpandableListAdapter;
+import android.widget.ExpandableListView;
+import android.widget.SimpleCursorTreeAdapter;
+import android.widget.SimpleExpandableListAdapter;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+import java.util.Map;
+
+/**
+ * An activity that displays an expandable list of items by binding to a data
+ * source implementing the ExpandableListAdapter, and exposes event handlers
+ * when the user selects an item.
+ * <p>
+ * ExpandableListActivity hosts a
+ * {@link android.widget.ExpandableListView ExpandableListView} object that can
+ * be bound to different data sources that provide a two-levels of data (the
+ * top-level is group, and below each group are children). Binding, screen
+ * layout, and row layout are discussed in the following sections.
+ * <p>
+ * <strong>Screen Layout</strong>
+ * </p>
+ * <p>
+ * ExpandableListActivity has a default layout that consists of a single,
+ * full-screen, centered expandable list. However, if you desire, you can
+ * customize the screen layout by setting your own view layout with
+ * setContentView() in onCreate(). To do this, your own view MUST contain an
+ * ExpandableListView object with the id "@android:id/list" (or
+ * {@link android.R.id#list} if it's in code)
+ * <p>
+ * Optionally, your custom view can contain another view object of any type to
+ * display when the list view is empty. This "empty list" notifier must have an
+ * id "android:empty". Note that when an empty view is present, the expandable
+ * list view will be hidden when there is no data to display.
+ * <p>
+ * The following code demonstrates an (ugly) custom screen layout. It has a list
+ * with a green background, and an alternate red "no data" message.
+ * </p>
+ *
+ * <pre>
+ * &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+ * &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:orientation=&quot;vertical&quot;
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;fill_parent&quot;
+ * android:paddingLeft=&quot;8dp&quot;
+ * android:paddingRight=&quot;8dp&quot;&gt;
+ *
+ * &lt;ExpandableListView android:id=&quot;@id/android:list&quot;
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;fill_parent&quot;
+ * android:background=&quot;#00FF00&quot;
+ * android:layout_weight=&quot;1&quot;
+ * android:drawSelectorOnTop=&quot;false&quot;/&gt;
+ *
+ * &lt;TextView android:id=&quot;@id/android:empty&quot;
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;fill_parent&quot;
+ * android:background=&quot;#FF0000&quot;
+ * android:text=&quot;No data&quot;/&gt;
+ * &lt;/LinearLayout&gt;
+ * </pre>
+ *
+ * <p>
+ * <strong>Row Layout</strong>
+ * </p>
+ * The {@link ExpandableListAdapter} set in the {@link ExpandableListActivity}
+ * via {@link #setListAdapter(ExpandableListAdapter)} provides the {@link View}s
+ * for each row. This adapter has separate methods for providing the group
+ * {@link View}s and child {@link View}s. There are a couple provided
+ * {@link ExpandableListAdapter}s that simplify use of adapters:
+ * {@link SimpleCursorTreeAdapter} and {@link SimpleExpandableListAdapter}.
+ * <p>
+ * With these, you can specify the layout of individual rows for groups and
+ * children in the list. These constructor takes a few parameters that specify
+ * layout resources for groups and children. It also has additional parameters
+ * that let you specify which data field to associate with which object in the
+ * row layout resource. The {@link SimpleCursorTreeAdapter} fetches data from
+ * {@link Cursor}s and the {@link SimpleExpandableListAdapter} fetches data
+ * from {@link List}s of {@link Map}s.
+ * </p>
+ * <p>
+ * Android provides some standard row layout resources. These are in the
+ * {@link android.R.layout} class, and have names such as simple_list_item_1,
+ * simple_list_item_2, and two_line_list_item. The following layout XML is the
+ * source for the resource two_line_list_item, which displays two data
+ * fields,one above the other, for each list row.
+ * </p>
+ *
+ * <pre>
+ * &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+ * &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;wrap_content&quot;
+ * android:orientation=&quot;vertical&quot;&gt;
+ *
+ * &lt;TextView android:id=&quot;@+id/text1&quot;
+ * android:textSize=&quot;16sp&quot;
+ * android:textStyle=&quot;bold&quot;
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;wrap_content&quot;/&gt;
+ *
+ * &lt;TextView android:id=&quot;@+id/text2&quot;
+ * android:textSize=&quot;16sp&quot;
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;wrap_content&quot;/&gt;
+ * &lt;/LinearLayout&gt;
+ * </pre>
+ *
+ * <p>
+ * You must identify the data bound to each TextView object in this layout. The
+ * syntax for this is discussed in the next section.
+ * </p>
+ * <p>
+ * <strong>Binding to Data</strong>
+ * </p>
+ * <p>
+ * You bind the ExpandableListActivity's ExpandableListView object to data using
+ * a class that implements the
+ * {@link android.widget.ExpandableListAdapter ExpandableListAdapter} interface.
+ * Android provides two standard list adapters:
+ * {@link android.widget.SimpleExpandableListAdapter SimpleExpandableListAdapter}
+ * for static data (Maps), and
+ * {@link android.widget.SimpleCursorTreeAdapter SimpleCursorTreeAdapter} for
+ * Cursor query results.
+ * </p>
+ *
+ * @see #setListAdapter
+ * @see android.widget.ExpandableListView
+ */
+public class ExpandableListActivity extends Activity implements
+ OnCreateContextMenuListener,
+ ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupCollapseListener,
+ ExpandableListView.OnGroupExpandListener {
+ ExpandableListAdapter mAdapter;
+ ExpandableListView mList;
+ boolean mFinishedStart = false;
+
+ /**
+ * Override this to populate the context menu when an item is long pressed. menuInfo
+ * will contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo}
+ * whose packedPosition is a packed position
+ * that should be used with {@link ExpandableListView#getPackedPositionType(long)} and
+ * the other similar methods.
+ * <p>
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ }
+
+ /**
+ * Override this for receiving callbacks when a child has been clicked.
+ * <p>
+ * {@inheritDoc}
+ */
+ public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
+ int childPosition, long id) {
+ return false;
+ }
+
+ /**
+ * Override this for receiving callbacks when a group has been collapsed.
+ */
+ public void onGroupCollapse(int groupPosition) {
+ }
+
+ /**
+ * Override this for receiving callbacks when a group has been expanded.
+ */
+ public void onGroupExpand(int groupPosition) {
+ }
+
+ /**
+ * Ensures the expandable list view has been created before Activity restores all
+ * of the view states.
+ *
+ *@see Activity#onRestoreInstanceState(Bundle)
+ */
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ ensureList();
+ super.onRestoreInstanceState(state);
+ }
+
+ /**
+ * Updates the screen state (current list and other views) when the
+ * content changes.
+ *
+ * @see Activity#onContentChanged()
+ */
+ @Override
+ public void onContentChanged() {
+ super.onContentChanged();
+ View emptyView = findViewById(com.android.internal.R.id.empty);
+ mList = (ExpandableListView)findViewById(com.android.internal.R.id.list);
+ if (mList == null) {
+ throw new RuntimeException(
+ "Your content must have a ExpandableListView whose id attribute is " +
+ "'android.R.id.list'");
+ }
+ if (emptyView != null) {
+ mList.setEmptyView(emptyView);
+ }
+ mList.setOnChildClickListener(this);
+ mList.setOnGroupExpandListener(this);
+ mList.setOnGroupCollapseListener(this);
+
+ if (mFinishedStart) {
+ setListAdapter(mAdapter);
+ }
+ mFinishedStart = true;
+ }
+
+ /**
+ * Provide the adapter for the expandable list.
+ */
+ public void setListAdapter(ExpandableListAdapter adapter) {
+ synchronized (this) {
+ ensureList();
+ mAdapter = adapter;
+ mList.setAdapter(adapter);
+ }
+ }
+
+ /**
+ * Get the activity's expandable list view widget. This can be used to get the selection,
+ * set the selection, and many other useful functions.
+ *
+ * @see ExpandableListView
+ */
+ public ExpandableListView getExpandableListView() {
+ ensureList();
+ return mList;
+ }
+
+ /**
+ * Get the ExpandableListAdapter associated with this activity's
+ * ExpandableListView.
+ */
+ public ExpandableListAdapter getExpandableListAdapter() {
+ return mAdapter;
+ }
+
+ private void ensureList() {
+ if (mList != null) {
+ return;
+ }
+ setContentView(com.android.internal.R.layout.expandable_list_content);
+ }
+
+ /**
+ * Gets the ID of the currently selected group or child.
+ *
+ * @return The ID of the currently selected group or child.
+ */
+ public long getSelectedId() {
+ return mList.getSelectedId();
+ }
+
+ /**
+ * Gets the position (in packed position representation) of the currently
+ * selected group or child. Use
+ * {@link ExpandableListView#getPackedPositionType},
+ * {@link ExpandableListView#getPackedPositionGroup}, and
+ * {@link ExpandableListView#getPackedPositionChild} to unpack the returned
+ * packed position.
+ *
+ * @return A packed position representation containing the currently
+ * selected group or child's position and type.
+ */
+ public long getSelectedPosition() {
+ return mList.getSelectedPosition();
+ }
+
+ /**
+ * Sets the selection to the specified child. If the child is in a collapsed
+ * group, the group will only be expanded and child subsequently selected if
+ * shouldExpandGroup is set to true, otherwise the method will return false.
+ *
+ * @param groupPosition The position of the group that contains the child.
+ * @param childPosition The position of the child within the group.
+ * @param shouldExpandGroup Whether the child's group should be expanded if
+ * it is collapsed.
+ * @return Whether the selection was successfully set on the child.
+ */
+ public boolean setSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup) {
+ return mList.setSelectedChild(groupPosition, childPosition, shouldExpandGroup);
+ }
+
+ /**
+ * Sets the selection to the specified group.
+ * @param groupPosition The position of the group that should be selected.
+ */
+ public void setSelectedGroup(int groupPosition) {
+ mList.setSelectedGroup(groupPosition);
+ }
+
+}
+
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
new file mode 100644
index 0000000..cd3701f
--- /dev/null
+++ b/core/java/android/app/IActivityManager.java
@@ -0,0 +1,368 @@
+/*
+ * 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.app;
+
+import android.app.ActivityManager.MemoryInfo;
+import android.content.ComponentName;
+import android.content.ContentProviderNative;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.ProviderInfo;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.text.TextUtils;
+import android.os.Bundle;
+
+import java.util.List;
+
+/**
+ * System private API for talking with the activity manager service. This
+ * provides calls from the application back to the activity manager.
+ *
+ * {@hide}
+ */
+public interface IActivityManager extends IInterface {
+ public static final int START_DELIVERED_TO_TOP = 3;
+ public static final int START_TASK_TO_FRONT = 2;
+ public static final int START_RETURN_INTENT_TO_CALLER = 1;
+ public static final int START_SUCCESS = 0;
+ public static final int START_INTENT_NOT_RESOLVED = -1;
+ public static final int START_CLASS_NOT_FOUND = -2;
+ public static final int START_FORWARD_AND_REQUEST_CONFLICT = -3;
+ public static final int START_PERMISSION_DENIED = -4;
+ public int startActivity(IApplicationThread caller,
+ Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+ int grantedMode, IBinder resultTo, String resultWho, int requestCode,
+ boolean onlyIfNeeded, boolean debug) throws RemoteException;
+ public boolean startNextMatchingActivity(IBinder callingActivity,
+ Intent intent) throws RemoteException;
+ public boolean finishActivity(IBinder token, int code, Intent data)
+ throws RemoteException;
+ public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
+ public Intent registerReceiver(IApplicationThread caller,
+ IIntentReceiver receiver, IntentFilter filter,
+ String requiredPermission) throws RemoteException;
+ public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException;
+ public static final int BROADCAST_SUCCESS = 0;
+ public static final int BROADCAST_STICKY_CANT_HAVE_PERMISSION = -1;
+ public int broadcastIntent(IApplicationThread caller, Intent intent,
+ String resolvedType, IIntentReceiver resultTo, int resultCode,
+ String resultData, Bundle map, String requiredPermission,
+ boolean serialized, boolean sticky) throws RemoteException;
+ public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException;
+ /* oneway */
+ public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException;
+ public void setPersistent(IBinder token, boolean isPersistent) throws RemoteException;
+ public void attachApplication(IApplicationThread app) throws RemoteException;
+ /* oneway */
+ public void activityIdle(IBinder token) throws RemoteException;
+ public void activityPaused(IBinder token, Bundle state) throws RemoteException;
+ /* oneway */
+ public void activityStopped(IBinder token,
+ Bitmap thumbnail, CharSequence description) throws RemoteException;
+ /* oneway */
+ public void activityDestroyed(IBinder token) throws RemoteException;
+ public String getCallingPackage(IBinder token) throws RemoteException;
+ public ComponentName getCallingActivity(IBinder token) throws RemoteException;
+ public List getTasks(int maxNum, int flags,
+ IThumbnailReceiver receiver) throws RemoteException;
+ public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
+ int flags) throws RemoteException;
+ public List getServices(int maxNum, int flags) throws RemoteException;
+ public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
+ throws RemoteException;
+ public void moveTaskToFront(int task) throws RemoteException;
+ public void moveTaskToBack(int task) throws RemoteException;
+ public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
+ public void moveTaskBackwards(int task) throws RemoteException;
+ public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
+ public void finishOtherInstances(IBinder token, ComponentName className) throws RemoteException;
+ /* oneway */
+ public void reportThumbnail(IBinder token,
+ Bitmap thumbnail, CharSequence description) throws RemoteException;
+ public ContentProviderHolder getContentProvider(IApplicationThread caller,
+ String name) throws RemoteException;
+ public void removeContentProvider(IApplicationThread caller,
+ String name) throws RemoteException;
+ public void publishContentProviders(IApplicationThread caller,
+ List<ContentProviderHolder> providers) throws RemoteException;
+ public ComponentName startService(IApplicationThread caller, Intent service,
+ String resolvedType) throws RemoteException;
+ public int stopService(IApplicationThread caller, Intent service,
+ String resolvedType) throws RemoteException;
+ public boolean stopServiceToken(ComponentName className, IBinder token,
+ int startId) throws RemoteException;
+ public void setServiceForeground(ComponentName className, IBinder token,
+ boolean isForeground) throws RemoteException;
+ public int bindService(IApplicationThread caller, IBinder token,
+ Intent service, String resolvedType,
+ IServiceConnection connection, int flags) throws RemoteException;
+ public boolean unbindService(IServiceConnection connection) throws RemoteException;
+ public void publishService(IBinder token,
+ Intent intent, IBinder service) throws RemoteException;
+ public void unbindFinished(IBinder token, Intent service,
+ boolean doRebind) throws RemoteException;
+ /* oneway */
+ public void serviceDoneExecuting(IBinder token) throws RemoteException;
+ public IBinder peekService(Intent service, String resolvedType) throws RemoteException;
+
+ public boolean startInstrumentation(ComponentName className, String profileFile,
+ int flags, Bundle arguments, IInstrumentationWatcher watcher)
+ throws RemoteException;
+ public void finishInstrumentation(IApplicationThread target,
+ int resultCode, Bundle results) throws RemoteException;
+
+ public Configuration getConfiguration() throws RemoteException;
+ public void updateConfiguration(Configuration values) throws RemoteException;
+ public void setRequestedOrientation(IBinder token,
+ int requestedOrientation) throws RemoteException;
+ public int getRequestedOrientation(IBinder token) throws RemoteException;
+
+ public ComponentName getActivityClassForToken(IBinder token) throws RemoteException;
+ public String getPackageForToken(IBinder token) throws RemoteException;
+
+ public static final int INTENT_SENDER_BROADCAST = 1;
+ public static final int INTENT_SENDER_ACTIVITY = 2;
+ public static final int INTENT_SENDER_ACTIVITY_RESULT = 3;
+ public static final int INTENT_SENDER_SERVICE = 4;
+ public IIntentSender getIntentSender(int type,
+ String packageName, IBinder token, String resultWho,
+ int requestCode, Intent intent, String resolvedType, int flags) throws RemoteException;
+ public void cancelIntentSender(IIntentSender sender) throws RemoteException;
+ public boolean clearApplicationUserData(final String packageName,
+ final IPackageDataObserver observer) throws RemoteException;
+ public String getPackageForIntentSender(IIntentSender sender) throws RemoteException;
+
+ public void setProcessLimit(int max) throws RemoteException;
+ public int getProcessLimit() throws RemoteException;
+
+ public void setProcessForeground(IBinder token, int pid, boolean isForeground) throws RemoteException;
+
+ public int checkPermission(String permission, int pid, int uid)
+ throws RemoteException;
+
+ public int checkUriPermission(Uri uri, int pid, int uid, int mode)
+ throws RemoteException;
+ public void grantUriPermission(IApplicationThread caller, String targetPkg,
+ Uri uri, int mode) throws RemoteException;
+ public void revokeUriPermission(IApplicationThread caller, Uri uri,
+ int mode) throws RemoteException;
+
+ public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
+ throws RemoteException;
+
+ public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) throws RemoteException;
+
+ public void restartPackage(final String packageName) throws RemoteException;
+
+ // Note: probably don't want to allow applications access to these.
+ public void goingToSleep() throws RemoteException;
+ public void wakingUp() throws RemoteException;
+
+ public void unhandledBack() throws RemoteException;
+ public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException;
+ public void setDebugApp(
+ String packageName, boolean waitForDebugger, boolean persistent)
+ throws RemoteException;
+ public void setAlwaysFinish(boolean enabled) throws RemoteException;
+ public void setActivityWatcher(IActivityWatcher watcher)
+ throws RemoteException;
+
+ public void enterSafeMode() throws RemoteException;
+
+ public void noteWakeupAlarm(IIntentSender sender) throws RemoteException;
+
+ public boolean killPidsForMemory(int[] pids) throws RemoteException;
+
+ public void reportPss(IApplicationThread caller, int pss) throws RemoteException;
+
+ // Special low-level communication with activity manager.
+ public void startRunning(String pkg, String cls, String action,
+ String data) throws RemoteException;
+ public void systemReady() throws RemoteException;
+ // Returns 1 if the user wants to debug.
+ public int handleApplicationError(IBinder app,
+ int flags, /* 1 == can debug */
+ String tag, String shortMsg, String longMsg,
+ byte[] crashData) throws RemoteException;
+
+ /*
+ * This will deliver the specified signal to all the persistent processes. Currently only
+ * SIGUSR1 is delivered. All others are ignored.
+ */
+ public void signalPersistentProcesses(int signal) throws RemoteException;
+ // Retrieve running application processes in the system
+ public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses()
+ throws RemoteException;
+ // Get device configuration
+ public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException;
+
+ /*
+ * Private non-Binder interfaces
+ */
+ /* package */ boolean testIsSystemReady();
+
+ /** Information you can retrieve about a particular application. */
+ public static class ContentProviderHolder implements Parcelable {
+ public final ProviderInfo info;
+ public final String permissionFailure;
+ public IContentProvider provider;
+ public boolean noReleaseNeeded;
+
+ public ContentProviderHolder(ProviderInfo _info) {
+ info = _info;
+ permissionFailure = null;
+ }
+
+ public ContentProviderHolder(ProviderInfo _info,
+ String _permissionFailure) {
+ info = _info;
+ permissionFailure = _permissionFailure;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ info.writeToParcel(dest, 0);
+ dest.writeString(permissionFailure);
+ if (provider != null) {
+ dest.writeStrongBinder(provider.asBinder());
+ } else {
+ dest.writeStrongBinder(null);
+ }
+ dest.writeInt(noReleaseNeeded ? 1:0);
+ }
+
+ public static final Parcelable.Creator<ContentProviderHolder> CREATOR
+ = new Parcelable.Creator<ContentProviderHolder>() {
+ public ContentProviderHolder createFromParcel(Parcel source) {
+ return new ContentProviderHolder(source);
+ }
+
+ public ContentProviderHolder[] newArray(int size) {
+ return new ContentProviderHolder[size];
+ }
+ };
+
+ private ContentProviderHolder(Parcel source) {
+ info = ProviderInfo.CREATOR.createFromParcel(source);
+ permissionFailure = source.readString();
+ provider = ContentProviderNative.asInterface(
+ source.readStrongBinder());
+ noReleaseNeeded = source.readInt() != 0;
+ }
+ };
+
+ String descriptor = "android.app.IActivityManager";
+
+ // Please keep these transaction codes the same -- they are also
+ // sent by C++ code.
+ int START_RUNNING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
+ int HANDLE_APPLICATION_ERROR_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
+ int START_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
+ int UNHANDLED_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
+ int OPEN_CONTENT_URI_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
+
+ // Remaining non-native transaction codes.
+ int FINISH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+10;
+ int REGISTER_RECEIVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+11;
+ int UNREGISTER_RECEIVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+12;
+ int BROADCAST_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+13;
+ int UNBROADCAST_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+14;
+ int FINISH_RECEIVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+15;
+ int ATTACH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+16;
+ int ACTIVITY_IDLE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+17;
+ int ACTIVITY_PAUSED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+18;
+ int ACTIVITY_STOPPED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+19;
+ int GET_CALLING_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+20;
+ int GET_CALLING_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+21;
+ int GET_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+22;
+ int MOVE_TASK_TO_FRONT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+23;
+ int MOVE_TASK_TO_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+24;
+ int MOVE_TASK_BACKWARDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+25;
+ int GET_TASK_FOR_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26;
+ int REPORT_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
+ int GET_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
+ int PUBLISH_CONTENT_PROVIDERS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
+ int SET_PERSISTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
+ int FINISH_SUB_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
+ int SYSTEM_READY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
+ int START_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
+ int STOP_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
+ int BIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
+ int UNBIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
+ int PUBLISH_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
+ int FINISH_OTHER_INSTANCES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38;
+ int GOING_TO_SLEEP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39;
+ int WAKING_UP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40;
+ int SET_DEBUG_APP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+41;
+ int SET_ALWAYS_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+42;
+ int START_INSTRUMENTATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43;
+ int FINISH_INSTRUMENTATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44;
+ int GET_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
+ int UPDATE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
+ int STOP_SERVICE_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
+ int GET_ACTIVITY_CLASS_FOR_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48;
+ int GET_PACKAGE_FOR_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49;
+ int SET_PROCESS_LIMIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+50;
+ int GET_PROCESS_LIMIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+51;
+ int CHECK_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+52;
+ int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+53;
+ int GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+54;
+ int REVOKE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+55;
+ int SET_ACTIVITY_WATCHER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+56;
+ int SHOW_WAITING_FOR_DEBUGGER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+57;
+ int SIGNAL_PERSISTENT_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+58;
+ int GET_RECENT_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+59;
+ int SERVICE_DONE_EXECUTING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+60;
+ int ACTIVITY_DESTROYED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+61;
+ int GET_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+62;
+ int CANCEL_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+63;
+ int GET_PACKAGE_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+64;
+ int ENTER_SAFE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+65;
+ int START_NEXT_MATCHING_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+66;
+ int NOTE_WAKEUP_ALARM_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+67;
+ int REMOVE_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+68;
+ int SET_REQUESTED_ORIENTATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+69;
+ int GET_REQUESTED_ORIENTATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+70;
+ int UNBIND_FINISHED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+71;
+ int SET_PROCESS_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+72;
+ int SET_SERVICE_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+73;
+ int MOVE_ACTIVITY_TASK_TO_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+74;
+ int GET_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+75;
+ int GET_PROCESSES_IN_ERROR_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+76;
+ int CLEAR_APP_DATA_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+77;
+ int RESTART_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78;
+ int KILL_PIDS_FOR_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
+ int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80;
+ int REPORT_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81;
+ int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82;
+ int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83;
+ int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84;
+}
diff --git a/core/java/android/app/IActivityPendingResult.aidl b/core/java/android/app/IActivityPendingResult.aidl
new file mode 100644
index 0000000..e8eebf1
--- /dev/null
+++ b/core/java/android/app/IActivityPendingResult.aidl
@@ -0,0 +1,27 @@
+/* //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.app;
+
+import android.os.Bundle;
+
+/** @hide */
+interface IActivityPendingResult
+{
+ boolean sendResult(int code, String data, in Bundle ex);
+}
+
diff --git a/core/java/android/app/IActivityWatcher.aidl b/core/java/android/app/IActivityWatcher.aidl
new file mode 100644
index 0000000..f13a385
--- /dev/null
+++ b/core/java/android/app/IActivityWatcher.aidl
@@ -0,0 +1,55 @@
+/* //device/java/android/android/app/IInstrumentationWatcher.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.app;
+
+import android.content.Intent;
+
+/**
+ * Testing interface to monitor what is happening in the activity manager
+ * while tests are running. Not for normal application development.
+ * {@hide}
+ */
+interface IActivityWatcher
+{
+ /**
+ * The system is trying to start an activity. Return true to allow
+ * it to be started as normal, or false to cancel/reject this activity.
+ */
+ boolean activityStarting(in Intent intent, String pkg);
+
+ /**
+ * The system is trying to return to an activity. Return true to allow
+ * it to be resumed as normal, or false to cancel/reject this activity.
+ */
+ boolean activityResuming(String pkg);
+
+ /**
+ * An application process has crashed (in Java). Return true for the
+ * normal error recovery (app crash dialog) to occur, false to kill
+ * it immediately.
+ */
+ boolean appCrashed(String processName, int pid, String shortMsg,
+ String longMsg, in byte[] crashData);
+
+ /**
+ * An application process is not responding. Return 0 to show the "app
+ * not responding" dialog, 1 to continue waiting, or -1 to kill it
+ * immediately.
+ */
+ int appNotResponding(String processName, int pid, String processStats);
+}
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
new file mode 100755
index 0000000..cb42236
--- /dev/null
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -0,0 +1,34 @@
+/* //device/java/android/android/app/IAlarmManager.aidl
+**
+** Copyright 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.app;
+
+import android.app.PendingIntent;
+
+/**
+ * System private API for talking with the alarm manager service.
+ *
+ * {@hide}
+ */
+interface IAlarmManager {
+ void set(int type, long triggerAtTime, in PendingIntent operation);
+ void setRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
+ void setInexactRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
+ void setTimeZone(String zone);
+ void remove(in PendingIntent operation);
+}
+
+
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
new file mode 100644
index 0000000..47476b5
--- /dev/null
+++ b/core/java/android/app/IApplicationThread.java
@@ -0,0 +1,118 @@
+/*
+ * 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.app;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.IInterface;
+
+import java.io.FileDescriptor;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * System private API for communicating with the application. This is given to
+ * the activity manager by an application when it starts up, for the activity
+ * manager to tell the application about things it needs to do.
+ *
+ * {@hide}
+ */
+public interface IApplicationThread extends IInterface {
+ void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving,
+ int configChanges) throws RemoteException;
+ void scheduleStopActivity(IBinder token, boolean showWindow,
+ int configChanges) throws RemoteException;
+ void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException;
+ void scheduleResumeActivity(IBinder token, boolean isForward) throws RemoteException;
+ void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
+ void scheduleLaunchActivity(Intent intent, IBinder token,
+ ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
+ List<Intent> pendingNewIntents, boolean notResumed, boolean isForward)
+ throws RemoteException;
+ void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
+ List<Intent> pendingNewIntents, int configChanges,
+ boolean notResumed) throws RemoteException;
+ void scheduleNewIntent(List<Intent> intent, IBinder token) throws RemoteException;
+ void scheduleDestroyActivity(IBinder token, boolean finished,
+ int configChanges) throws RemoteException;
+ void scheduleReceiver(Intent intent, ActivityInfo info, int resultCode,
+ String data, Bundle extras, boolean sync) throws RemoteException;
+ void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException;
+ void scheduleBindService(IBinder token,
+ Intent intent, boolean rebind) throws RemoteException;
+ void scheduleUnbindService(IBinder token,
+ Intent intent) throws RemoteException;
+ void scheduleServiceArgs(IBinder token, int startId, Intent args) throws RemoteException;
+ void scheduleStopService(IBinder token) throws RemoteException;
+ static final int DEBUG_OFF = 0;
+ static final int DEBUG_ON = 1;
+ static final int DEBUG_WAIT = 2;
+ void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
+ ComponentName testName, String profileName, Bundle testArguments,
+ IInstrumentationWatcher testWatcher, int debugMode, Configuration config, Map<String,
+ IBinder> services) throws RemoteException;
+ void scheduleExit() throws RemoteException;
+ void requestThumbnail(IBinder token) throws RemoteException;
+ void scheduleConfigurationChanged(Configuration config) throws RemoteException;
+ void updateTimeZone() throws RemoteException;
+ void processInBackground() throws RemoteException;
+ void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
+ throws RemoteException;
+ void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
+ int resultCode, String data, Bundle extras, boolean ordered)
+ throws RemoteException;
+ void scheduleLowMemory() throws RemoteException;
+ void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
+ void requestPss() throws RemoteException;
+
+ String descriptor = "android.app.IApplicationThread";
+
+ int SCHEDULE_PAUSE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
+ int SCHEDULE_STOP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
+ int SCHEDULE_WINDOW_VISIBILITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
+ int SCHEDULE_RESUME_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
+ int SCHEDULE_SEND_RESULT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+5;
+ int SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+6;
+ int SCHEDULE_NEW_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+7;
+ int SCHEDULE_FINISH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+8;
+ int SCHEDULE_RECEIVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+9;
+ int SCHEDULE_CREATE_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+10;
+ int SCHEDULE_STOP_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+11;
+ int BIND_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+12;
+ int SCHEDULE_EXIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+13;
+ int REQUEST_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+14;
+ int SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+15;
+ int SCHEDULE_SERVICE_ARGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+16;
+ int UPDATE_TIME_ZONE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+17;
+ int PROCESS_IN_BACKGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+18;
+ int SCHEDULE_BIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+19;
+ int SCHEDULE_UNBIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+20;
+ int DUMP_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+21;
+ int SCHEDULE_REGISTERED_RECEIVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+22;
+ int SCHEDULE_LOW_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+23;
+ int SCHEDULE_ACTIVITY_CONFIGURATION_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+24;
+ int SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+25;
+ int REQUEST_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26;
+}
diff --git a/core/java/android/app/IInstrumentationWatcher.aidl b/core/java/android/app/IInstrumentationWatcher.aidl
new file mode 100644
index 0000000..405a3d8
--- /dev/null
+++ b/core/java/android/app/IInstrumentationWatcher.aidl
@@ -0,0 +1,31 @@
+/* //device/java/android/android/app/IInstrumentationWatcher.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.app;
+
+import android.content.ComponentName;
+import android.os.Bundle;
+
+/** @hide */
+oneway interface IInstrumentationWatcher
+{
+ void instrumentationStatus(in ComponentName name, int resultCode,
+ in Bundle results);
+ void instrumentationFinished(in ComponentName name, int resultCode,
+ in Bundle results);
+}
+
diff --git a/core/java/android/app/IIntentReceiver.aidl b/core/java/android/app/IIntentReceiver.aidl
new file mode 100755
index 0000000..5f5d0eb
--- /dev/null
+++ b/core/java/android/app/IIntentReceiver.aidl
@@ -0,0 +1,33 @@
+/*
+**
+** Copyright 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.app;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * System private API for dispatching intent broadcasts. This is given to the
+ * activity manager as part of registering for an intent broadcasts, and is
+ * called when it receives intents.
+ *
+ * {@hide}
+ */
+oneway interface IIntentReceiver {
+ void performReceive(in Intent intent, int resultCode,
+ String data, in Bundle extras, boolean ordered);
+}
+
diff --git a/core/java/android/app/IIntentSender.aidl b/core/java/android/app/IIntentSender.aidl
new file mode 100644
index 0000000..53e135a
--- /dev/null
+++ b/core/java/android/app/IIntentSender.aidl
@@ -0,0 +1,27 @@
+/* //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.app;
+
+import android.app.IIntentReceiver;
+import android.content.Intent;
+
+/** @hide */
+interface IIntentSender {
+ int send(int code, in Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver);
+}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
new file mode 100644
index 0000000..c1035b6
--- /dev/null
+++ b/core/java/android/app/INotificationManager.aidl
@@ -0,0 +1,34 @@
+/* //device/java/android/android/app/INotificationManager.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.app;
+
+import android.app.ITransientNotification;
+import android.app.Notification;
+import android.content.Intent;
+
+/** {@hide} */
+interface INotificationManager
+{
+ void enqueueNotification(String pkg, int id, in Notification notification, inout int[] idReceived);
+ void cancelNotification(String pkg, int id);
+ void cancelAllNotifications(String pkg);
+
+ void enqueueToast(String pkg, ITransientNotification callback, int duration);
+ void cancelToast(String pkg, ITransientNotification callback);
+}
+
diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl
new file mode 100644
index 0000000..6c3617a
--- /dev/null
+++ b/core/java/android/app/ISearchManager.aidl
@@ -0,0 +1,25 @@
+/**
+ * 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.app;
+
+import android.content.ComponentName;
+import android.server.search.SearchableInfo;
+
+/** @hide */
+interface ISearchManager {
+ SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch);
+}
diff --git a/core/java/android/app/IServiceConnection.aidl b/core/java/android/app/IServiceConnection.aidl
new file mode 100644
index 0000000..6804071
--- /dev/null
+++ b/core/java/android/app/IServiceConnection.aidl
@@ -0,0 +1,26 @@
+/* //device/java/android/android/app/IServiceConnection.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.app;
+
+import android.content.ComponentName;
+
+/** @hide */
+oneway interface IServiceConnection {
+ void connected(in ComponentName name, IBinder service);
+}
+
diff --git a/core/java/android/app/IStatusBar.aidl b/core/java/android/app/IStatusBar.aidl
new file mode 100644
index 0000000..c64fa50
--- /dev/null
+++ b/core/java/android/app/IStatusBar.aidl
@@ -0,0 +1,29 @@
+/**
+ * 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.app;
+
+/** @hide */
+interface IStatusBar
+{
+ void activate();
+ void deactivate();
+ void toggle();
+ void disable(int what, IBinder token, String pkg);
+ IBinder addIcon(String slot, String iconPackage, int iconId, int iconLevel);
+ void updateIcon(IBinder key, String slot, String iconPackage, int iconId, int iconLevel);
+ void removeIcon(IBinder key);
+}
diff --git a/core/java/android/app/IThumbnailReceiver.aidl b/core/java/android/app/IThumbnailReceiver.aidl
new file mode 100755
index 0000000..7943f2c
--- /dev/null
+++ b/core/java/android/app/IThumbnailReceiver.aidl
@@ -0,0 +1,30 @@
+/* //device/java/android/android/app/IThumbnailReceiver.aidl
+**
+** Copyright 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.app;
+
+import android.graphics.Bitmap;
+
+/**
+ * System private API for receiving updated thumbnails from a checkpoint.
+ *
+ * {@hide}
+ */
+oneway interface IThumbnailReceiver {
+ void newThumbnail(int id, in Bitmap thumbnail, CharSequence description);
+ void finished();
+}
+
diff --git a/core/java/android/app/ITransientNotification.aidl b/core/java/android/app/ITransientNotification.aidl
new file mode 100644
index 0000000..35b53a4
--- /dev/null
+++ b/core/java/android/app/ITransientNotification.aidl
@@ -0,0 +1,25 @@
+/* //device/java/android/android/app/ITransientNotification.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.app;
+
+/** @hide */
+oneway interface ITransientNotification {
+ void show();
+ void hide();
+}
+
diff --git a/core/java/android/app/IWallpaperService.aidl b/core/java/android/app/IWallpaperService.aidl
new file mode 100644
index 0000000..a332b1a
--- /dev/null
+++ b/core/java/android/app/IWallpaperService.aidl
@@ -0,0 +1,55 @@
+/**
+ * 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.app;
+
+import android.os.ParcelFileDescriptor;
+import android.app.IWallpaperServiceCallback;
+
+/** @hide */
+interface IWallpaperService {
+
+ /**
+ * Set the wallpaper.
+ */
+ ParcelFileDescriptor setWallpaper();
+
+ /**
+ * Get the wallpaper.
+ */
+ ParcelFileDescriptor getWallpaper(IWallpaperServiceCallback cb);
+
+ /**
+ * Clear the wallpaper.
+ */
+ void clearWallpaper();
+
+ /**
+ * Sets the dimension hint for the wallpaper. These hints indicate the desired
+ * minimum width and height for the wallpaper.
+ */
+ void setDimensionHints(in int width, in int height);
+
+ /**
+ * Returns the desired minimum width for the wallpaper.
+ */
+ int getWidthHint();
+
+ /**
+ * Returns the desired minimum height for the wallpaper.
+ */
+ int getHeightHint();
+}
diff --git a/core/java/android/app/IWallpaperServiceCallback.aidl b/core/java/android/app/IWallpaperServiceCallback.aidl
new file mode 100644
index 0000000..6086f40
--- /dev/null
+++ b/core/java/android/app/IWallpaperServiceCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.app;
+
+/**
+ * Callback interface used by IWallpaperService to send asynchronous
+ * notifications back to its clients. Note that this is a
+ * one-way interface so the server does not block waiting for the client.
+ *
+ * @hide
+ */
+oneway interface IWallpaperServiceCallback {
+ /**
+ * Called when the wallpaper has changed
+ */
+ void onWallpaperChanged();
+}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
new file mode 100644
index 0000000..f6a28b2
--- /dev/null
+++ b/core/java/android/app/Instrumentation.java
@@ -0,0 +1,1613 @@
+/*
+ * 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.app;
+
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.Debug;
+import android.os.IBinder;
+import android.os.MessageQueue;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.ServiceManager;
+import android.util.AndroidRuntimeException;
+import android.util.Config;
+import android.util.Log;
+import android.view.IWindowManager;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.view.Window;
+import android.view.inputmethod.InputMethodManager;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Base class for implementing application instrumentation code. When running
+ * with instrumentation turned on, this class will be instantiated for you
+ * before any of the application code, allowing you to monitor all of the
+ * interaction the system has with the application. An Instrumentation
+ * implementation is described to the system through an AndroidManifest.xml's
+ * &lt;instrumentation&gt; tag.
+ */
+public class Instrumentation {
+ /**
+ * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
+ * identifies the class that is writing the report. This can be used to provide more structured
+ * logging or reporting capabilities in the IInstrumentationWatcher.
+ */
+ public static final String REPORT_KEY_IDENTIFIER = "id";
+ /**
+ * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
+ * identifies a string which can simply be printed to the output stream. Using these streams
+ * provides a "pretty printer" version of the status & final packets. Any bundles including
+ * this key should also include the complete set of raw key/value pairs, so that the
+ * instrumentation can also be launched, and results collected, by an automated system.
+ */
+ public static final String REPORT_KEY_STREAMRESULT = "stream";
+
+ private static final String TAG = "Instrumentation";
+
+ private final Object mSync = new Object();
+ private ActivityThread mThread = null;
+ private MessageQueue mMessageQueue = null;
+ private Context mInstrContext;
+ private Context mAppContext;
+ private ComponentName mComponent;
+ private Thread mRunner;
+ private List<ActivityWaiter> mWaitingActivities;
+ private List<ActivityMonitor> mActivityMonitors;
+ private IInstrumentationWatcher mWatcher;
+ private long mPreCpuTime;
+ private long mStart;
+ private boolean mAutomaticPerformanceSnapshots = false;
+ private Bundle mPrePerfMetrics = new Bundle();
+ private Bundle mPerfMetrics = new Bundle();
+
+ public Instrumentation() {
+ }
+
+ /**
+ * Called when the instrumentation is starting, before any application code
+ * has been loaded. Usually this will be implemented to simply call
+ * {@link #start} to begin the instrumentation thread, which will then
+ * continue execution in {@link #onStart}.
+ *
+ * <p>If you do not need your own thread -- that is you are writing your
+ * instrumentation to be completely asynchronous (returning to the event
+ * loop so that the application can run), you can simply begin your
+ * instrumentation here, for example call {@link Context#startActivity} to
+ * begin the appropriate first activity of the application.
+ *
+ * @param arguments Any additional arguments that were supplied when the
+ * instrumentation was started.
+ */
+ public void onCreate(Bundle arguments) {
+ }
+
+ /**
+ * Create and start a new thread in which to run instrumentation. This new
+ * thread will call to {@link #onStart} where you can implement the
+ * instrumentation.
+ */
+ public void start() {
+ if (mRunner != null) {
+ throw new RuntimeException("Instrumentation already started");
+ }
+ mRunner = new InstrumentationThread("Instr: " + getClass().getName());
+ mRunner.start();
+ }
+
+ /**
+ * Method where the instrumentation thread enters execution. This allows
+ * you to run your instrumentation code in a separate thread than the
+ * application, so that it can perform blocking operation such as
+ * {@link #sendKeySync} or {@link #startActivitySync}.
+ *
+ * <p>You will typically want to call finish() when this function is done,
+ * to end your instrumentation.
+ */
+ public void onStart() {
+ }
+
+ /**
+ * This is called whenever the system captures an unhandled exception that
+ * was thrown by the application. The default implementation simply
+ * returns false, allowing normal system handling of the exception to take
+ * place.
+ *
+ * @param obj The client object that generated the exception. May be an
+ * Application, Activity, BroadcastReceiver, Service, or null.
+ * @param e The exception that was thrown.
+ *
+ * @return To allow normal system exception process to occur, return false.
+ * If true is returned, the system will proceed as if the exception
+ * didn't happen.
+ */
+ public boolean onException(Object obj, Throwable e) {
+ return false;
+ }
+
+ /**
+ * Provide a status report about the application.
+ *
+ * @param resultCode Current success/failure of instrumentation.
+ * @param results Any results to send back to the code that started the instrumentation.
+ */
+ public void sendStatus(int resultCode, Bundle results) {
+ if (mWatcher != null) {
+ try {
+ mWatcher.instrumentationStatus(mComponent, resultCode, results);
+ }
+ catch (RemoteException e) {
+ mWatcher = null;
+ }
+ }
+ }
+
+ /**
+ * Terminate instrumentation of the application. This will cause the
+ * application process to exit, removing this instrumentation from the next
+ * time the application is started.
+ *
+ * @param resultCode Overall success/failure of instrumentation.
+ * @param results Any results to send back to the code that started the
+ * instrumentation.
+ */
+ public void finish(int resultCode, Bundle results) {
+ if (mAutomaticPerformanceSnapshots) {
+ endPerformanceSnapshot();
+ }
+ if (mPerfMetrics != null) {
+ results.putAll(mPerfMetrics);
+ }
+ mThread.finishInstrumentation(resultCode, results);
+ }
+
+ public void setAutomaticPerformanceSnapshots() {
+ mAutomaticPerformanceSnapshots = true;
+ }
+
+ public void startPerformanceSnapshot() {
+ mStart = 0;
+ if (!isProfiling()) {
+ // Add initial binder counts
+ Bundle binderCounts = getBinderCounts();
+ for (String key: binderCounts.keySet()) {
+ addPerfMetricLong("pre_" + key, binderCounts.getLong(key));
+ }
+
+ // Force a GC and zero out the performance counters. Do this
+ // before reading initial CPU/wall-clock times so we don't include
+ // the cost of this setup in our final metrics.
+ startAllocCounting();
+
+ // Record CPU time up to this point, and start timing. Note: this
+ // must happen at the end of this method, otherwise the timing will
+ // include noise.
+ mStart = SystemClock.uptimeMillis();
+ mPreCpuTime = Process.getElapsedCpuTime();
+ }
+ }
+
+ public void endPerformanceSnapshot() {
+ if (!isProfiling()) {
+ // Stop the timing. This must be done first before any other counting is stopped.
+ long cpuTime = Process.getElapsedCpuTime();
+ long duration = SystemClock.uptimeMillis();
+
+ stopAllocCounting();
+
+ long nativeMax = Debug.getNativeHeapSize() / 1024;
+ long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
+ long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
+
+ Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
+ Debug.getMemoryInfo(memInfo);
+
+ Runtime runtime = Runtime.getRuntime();
+
+ long dalvikMax = runtime.totalMemory() / 1024;
+ long dalvikFree = runtime.freeMemory() / 1024;
+ long dalvikAllocated = dalvikMax - dalvikFree;
+
+ // Add final binder counts
+ Bundle binderCounts = getBinderCounts();
+ for (String key: binderCounts.keySet()) {
+ addPerfMetricLong(key, binderCounts.getLong(key));
+ }
+
+ // Add alloc counts
+ Bundle allocCounts = getAllocCounts();
+ for (String key: allocCounts.keySet()) {
+ addPerfMetricLong(key, allocCounts.getLong(key));
+ }
+
+ addPerfMetricLong("execution_time", duration - mStart);
+ addPerfMetricLong("pre_cpu_time", mPreCpuTime);
+ addPerfMetricLong("cpu_time", cpuTime - mPreCpuTime);
+
+ addPerfMetricLong("native_size", nativeMax);
+ addPerfMetricLong("native_allocated", nativeAllocated);
+ addPerfMetricLong("native_free", nativeFree);
+ addPerfMetricInt("native_pss", memInfo.nativePss);
+ addPerfMetricInt("native_private_dirty", memInfo.nativePrivateDirty);
+ addPerfMetricInt("native_shared_dirty", memInfo.nativeSharedDirty);
+
+ addPerfMetricLong("java_size", dalvikMax);
+ addPerfMetricLong("java_allocated", dalvikAllocated);
+ addPerfMetricLong("java_free", dalvikFree);
+ addPerfMetricInt("java_pss", memInfo.dalvikPss);
+ addPerfMetricInt("java_private_dirty", memInfo.dalvikPrivateDirty);
+ addPerfMetricInt("java_shared_dirty", memInfo.dalvikSharedDirty);
+
+ addPerfMetricInt("other_pss", memInfo.otherPss);
+ addPerfMetricInt("other_private_dirty", memInfo.otherPrivateDirty);
+ addPerfMetricInt("other_shared_dirty", memInfo.otherSharedDirty);
+
+ }
+ }
+
+ private void addPerfMetricLong(String key, long value) {
+ mPerfMetrics.putLong("performance." + key, value);
+ }
+
+ private void addPerfMetricInt(String key, int value) {
+ mPerfMetrics.putInt("performance." + key, value);
+ }
+
+ /**
+ * Called when the instrumented application is stopping, after all of the
+ * normal application cleanup has occurred.
+ */
+ public void onDestroy() {
+ }
+
+ /**
+ * Return the Context of this instrumentation's package. Note that this is
+ * often different than the Context of the application being
+ * instrumentated, since the instrumentation code often lives is a
+ * different package than that of the application it is running against.
+ * See {@link #getTargetContext} to retrieve a Context for the target
+ * application.
+ *
+ * @return The instrumentation's package context.
+ *
+ * @see #getTargetContext
+ */
+ public Context getContext() {
+ return mInstrContext;
+ }
+
+ /**
+ * Returns complete component name of this instrumentation.
+ *
+ * @return Returns the complete component name for this instrumentation.
+ */
+ public ComponentName getComponentName() {
+ return mComponent;
+ }
+
+ /**
+ * Return a Context for the target application being instrumented. Note
+ * that this is often different than the Context of the instrumentation
+ * code, since the instrumentation code often lives is a different package
+ * than that of the application it is running against. See
+ * {@link #getContext} to retrieve a Context for the instrumentation code.
+ *
+ * @return A Context in the target application.
+ *
+ * @see #getContext
+ */
+ public Context getTargetContext() {
+ return mAppContext;
+ }
+
+ /**
+ * Check whether this instrumentation was started with profiling enabled.
+ *
+ * @return Returns true if profiling was enabled when starting, else false.
+ */
+ public boolean isProfiling() {
+ return mThread.isProfiling();
+ }
+
+ /**
+ * This method will start profiling if isProfiling() returns true. You should
+ * only call this method if you set the handleProfiling attribute in the
+ * manifest file for this Instrumentation to true.
+ */
+ public void startProfiling() {
+ if (mThread.isProfiling()) {
+ File file = new File(mThread.getProfileFilePath());
+ file.getParentFile().mkdirs();
+ Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
+ }
+ }
+
+ /**
+ * Stops profiling if isProfiling() returns true.
+ */
+ public void stopProfiling() {
+ if (mThread.isProfiling()) {
+ Debug.stopMethodTracing();
+ }
+ }
+
+ /**
+ * Force the global system in or out of touch mode. This can be used if
+ * your instrumentation relies on the UI being in one more or the other
+ * when it starts.
+ *
+ * @param inTouch Set to true to be in touch mode, false to be in
+ * focus mode.
+ */
+ public void setInTouchMode(boolean inTouch) {
+ try {
+ IWindowManager.Stub.asInterface(
+ ServiceManager.getService("window")).setInTouchMode(inTouch);
+ } catch (RemoteException e) {
+ // Shouldn't happen!
+ }
+ }
+
+ /**
+ * Schedule a callback for when the application's main thread goes idle
+ * (has no more events to process).
+ *
+ * @param recipient Called the next time the thread's message queue is
+ * idle.
+ */
+ public void waitForIdle(Runnable recipient) {
+ mMessageQueue.addIdleHandler(new Idler(recipient));
+ mThread.getHandler().post(new EmptyRunnable());
+ }
+
+ /**
+ * Synchronously wait for the application to be idle. Can not be called
+ * from the main application thread -- use {@link #start} to execute
+ * instrumentation in its own thread.
+ */
+ public void waitForIdleSync() {
+ validateNotAppThread();
+ Idler idler = new Idler(null);
+ mMessageQueue.addIdleHandler(idler);
+ mThread.getHandler().post(new EmptyRunnable());
+ idler.waitForIdle();
+ }
+
+ /**
+ * Execute a call on the application's main thread, blocking until it is
+ * complete. Useful for doing things that are not thread-safe, such as
+ * looking at or modifying the view hierarchy.
+ *
+ * @param runner The code to run on the main thread.
+ */
+ public void runOnMainSync(Runnable runner) {
+ validateNotAppThread();
+ SyncRunnable sr = new SyncRunnable(runner);
+ mThread.getHandler().post(sr);
+ sr.waitForComplete();
+ }
+
+ /**
+ * Start a new activity and wait for it to begin running before returning.
+ * In addition to being synchronous, this method as some semantic
+ * differences from the standard {@link Context#startActivity} call: the
+ * activity component is resolved before talking with the activity manager
+ * (its class name is specified in the Intent that this method ultimately
+ * starts), and it does not allow you to start activities that run in a
+ * different process. In addition, if the given Intent resolves to
+ * multiple activities, instead of displaying a dialog for the user to
+ * select an activity, an exception will be thrown.
+ *
+ * <p>The function returns as soon as the activity goes idle following the
+ * call to its {@link Activity#onCreate}. Generally this means it has gone
+ * through the full initialization including {@link Activity#onResume} and
+ * drawn and displayed its initial window.
+ *
+ * @param intent Description of the activity to start.
+ *
+ * @see Context#startActivity
+ */
+ public Activity startActivitySync(Intent intent) {
+ validateNotAppThread();
+
+ synchronized (mSync) {
+ intent = new Intent(intent);
+
+ ActivityInfo ai = intent.resolveActivityInfo(
+ getTargetContext().getPackageManager(), 0);
+ if (ai == null) {
+ throw new RuntimeException("Unable to resolve activity for: " + intent);
+ }
+ if (!ai.applicationInfo.processName.equals(
+ getTargetContext().getPackageName())) {
+ // todo: if this intent is ambiguous, look here to see if
+ // there is a single match that is in our package.
+ throw new RuntimeException("Intent resolved to different package "
+ + ai.applicationInfo.packageName + ": "
+ + intent);
+ }
+
+ intent.setComponent(new ComponentName(
+ ai.applicationInfo.packageName, ai.name));
+ final ActivityWaiter aw = new ActivityWaiter(intent);
+
+ if (mWaitingActivities == null) {
+ mWaitingActivities = new ArrayList();
+ }
+ mWaitingActivities.add(aw);
+
+ getTargetContext().startActivity(intent);
+
+ do {
+ try {
+ mSync.wait();
+ } catch (InterruptedException e) {
+ }
+ } while (mWaitingActivities.contains(aw));
+
+ return aw.activity;
+ }
+ }
+
+ /**
+ * Information about a particular kind of Intent that is being monitored.
+ * An instance of this class is added to the
+ * current instrumentation through {@link #addMonitor}; after being added,
+ * when a new activity is being started the monitor will be checked and, if
+ * matching, its hit count updated and (optionally) the call stopped and a
+ * canned result returned.
+ *
+ * <p>An ActivityMonitor can also be used to look for the creation of an
+ * activity, through the {@link #waitForActivity} method. This will return
+ * after a matching activity has been created with that activity object.
+ */
+ public static class ActivityMonitor {
+ private final IntentFilter mWhich;
+ private final String mClass;
+ private final ActivityResult mResult;
+ private final boolean mBlock;
+
+
+ // This is protected by 'Instrumentation.this.mSync'.
+ /*package*/ int mHits = 0;
+
+ // This is protected by 'this'.
+ /*package*/ Activity mLastActivity = null;
+
+ /**
+ * Create a new ActivityMonitor that looks for a particular kind of
+ * intent to be started.
+ *
+ * @param which The set of intents this monitor is responsible for.
+ * @param result A canned result to return if the monitor is hit; can
+ * be null.
+ * @param block Controls whether the monitor should block the activity
+ * start (returning its canned result) or let the call
+ * proceed.
+ *
+ * @see Instrumentation#addMonitor
+ */
+ public ActivityMonitor(
+ IntentFilter which, ActivityResult result, boolean block) {
+ mWhich = which;
+ mClass = null;
+ mResult = result;
+ mBlock = block;
+ }
+
+ /**
+ * Create a new ActivityMonitor that looks for a specific activity
+ * class to be started.
+ *
+ * @param cls The activity class this monitor is responsible for.
+ * @param result A canned result to return if the monitor is hit; can
+ * be null.
+ * @param block Controls whether the monitor should block the activity
+ * start (returning its canned result) or let the call
+ * proceed.
+ *
+ * @see Instrumentation#addMonitor
+ */
+ public ActivityMonitor(
+ String cls, ActivityResult result, boolean block) {
+ mWhich = null;
+ mClass = cls;
+ mResult = result;
+ mBlock = block;
+ }
+
+ /**
+ * Retrieve the filter associated with this ActivityMonitor.
+ */
+ public final IntentFilter getFilter() {
+ return mWhich;
+ }
+
+ /**
+ * Retrieve the result associated with this ActivityMonitor, or null if
+ * none.
+ */
+ public final ActivityResult getResult() {
+ return mResult;
+ }
+
+ /**
+ * Check whether this monitor blocks activity starts (not allowing the
+ * actual activity to run) or allows them to execute normally.
+ */
+ public final boolean isBlocking() {
+ return mBlock;
+ }
+
+ /**
+ * Retrieve the number of times the monitor has been hit so far.
+ */
+ public final int getHits() {
+ return mHits;
+ }
+
+ /**
+ * Retrieve the most recent activity class that was seen by this
+ * monitor.
+ */
+ public final Activity getLastActivity() {
+ return mLastActivity;
+ }
+
+ /**
+ * Block until an Activity is created that matches this monitor,
+ * returning the resulting activity.
+ *
+ * @return Activity
+ */
+ public final Activity waitForActivity() {
+ synchronized (this) {
+ while (mLastActivity == null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ Activity res = mLastActivity;
+ mLastActivity = null;
+ return res;
+ }
+ }
+
+ /**
+ * Block until an Activity is created that matches this monitor,
+ * returning the resulting activity or till the timeOut period expires.
+ * If the timeOut expires before the activity is started, return null.
+ *
+ * @param timeOut Time to wait before the activity is created.
+ *
+ * @return Activity
+ */
+ public final Activity waitForActivityWithTimeout(long timeOut) {
+ synchronized (this) {
+ try {
+ wait(timeOut);
+ } catch (InterruptedException e) {
+ }
+ if (mLastActivity == null) {
+ return null;
+ } else {
+ Activity res = mLastActivity;
+ mLastActivity = null;
+ return res;
+ }
+ }
+ }
+
+ final boolean match(Context who,
+ Activity activity,
+ Intent intent) {
+ synchronized (this) {
+ if (mWhich != null
+ && mWhich.match(who.getContentResolver(), intent,
+ true, "Instrumentation") < 0) {
+ return false;
+ }
+ if (mClass != null) {
+ String cls = null;
+ if (activity != null) {
+ cls = activity.getClass().getName();
+ } else if (intent.getComponent() != null) {
+ cls = intent.getComponent().getClassName();
+ }
+ if (cls == null || !mClass.equals(cls)) {
+ return false;
+ }
+ }
+ if (activity != null) {
+ mLastActivity = activity;
+ notifyAll();
+ }
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Add a new {@link ActivityMonitor} that will be checked whenever an
+ * activity is started. The monitor is added
+ * after any existing ones; the monitor will be hit only if none of the
+ * existing monitors can themselves handle the Intent.
+ *
+ * @param monitor The new ActivityMonitor to see.
+ *
+ * @see #addMonitor(IntentFilter, ActivityResult, boolean)
+ * @see #checkMonitorHit
+ */
+ public void addMonitor(ActivityMonitor monitor) {
+ synchronized (mSync) {
+ if (mActivityMonitors == null) {
+ mActivityMonitors = new ArrayList();
+ }
+ mActivityMonitors.add(monitor);
+ }
+ }
+
+ /**
+ * A convenience wrapper for {@link #addMonitor(ActivityMonitor)} that
+ * creates an intent filter matching {@link ActivityMonitor} for you and
+ * returns it.
+ *
+ * @param filter The set of intents this monitor is responsible for.
+ * @param result A canned result to return if the monitor is hit; can
+ * be null.
+ * @param block Controls whether the monitor should block the activity
+ * start (returning its canned result) or let the call
+ * proceed.
+ *
+ * @return The newly created and added activity monitor.
+ *
+ * @see #addMonitor(ActivityMonitor)
+ * @see #checkMonitorHit
+ */
+ public ActivityMonitor addMonitor(
+ IntentFilter filter, ActivityResult result, boolean block) {
+ ActivityMonitor am = new ActivityMonitor(filter, result, block);
+ addMonitor(am);
+ return am;
+ }
+
+ /**
+ * A convenience wrapper for {@link #addMonitor(ActivityMonitor)} that
+ * creates a class matching {@link ActivityMonitor} for you and returns it.
+ *
+ * @param cls The activity class this monitor is responsible for.
+ * @param result A canned result to return if the monitor is hit; can
+ * be null.
+ * @param block Controls whether the monitor should block the activity
+ * start (returning its canned result) or let the call
+ * proceed.
+ *
+ * @return The newly created and added activity monitor.
+ *
+ * @see #addMonitor(ActivityMonitor)
+ * @see #checkMonitorHit
+ */
+ public ActivityMonitor addMonitor(
+ String cls, ActivityResult result, boolean block) {
+ ActivityMonitor am = new ActivityMonitor(cls, result, block);
+ addMonitor(am);
+ return am;
+ }
+
+ /**
+ * Test whether an existing {@link ActivityMonitor} has been hit. If the
+ * monitor has been hit at least <var>minHits</var> times, then it will be
+ * removed from the activity monitor list and true returned. Otherwise it
+ * is left as-is and false is returned.
+ *
+ * @param monitor The ActivityMonitor to check.
+ * @param minHits The minimum number of hits required.
+ *
+ * @return True if the hit count has been reached, else false.
+ *
+ * @see #addMonitor
+ */
+ public boolean checkMonitorHit(ActivityMonitor monitor, int minHits) {
+ waitForIdleSync();
+ synchronized (mSync) {
+ if (monitor.getHits() < minHits) {
+ return false;
+ }
+ mActivityMonitors.remove(monitor);
+ }
+ return true;
+ }
+
+ /**
+ * Wait for an existing {@link ActivityMonitor} to be hit. Once the
+ * monitor has been hit, it is removed from the activity monitor list and
+ * the first created Activity object that matched it is returned.
+ *
+ * @param monitor The ActivityMonitor to wait for.
+ *
+ * @return The Activity object that matched the monitor.
+ */
+ public Activity waitForMonitor(ActivityMonitor monitor) {
+ Activity activity = monitor.waitForActivity();
+ synchronized (mSync) {
+ mActivityMonitors.remove(monitor);
+ }
+ return activity;
+ }
+
+ /**
+ * Wait for an existing {@link ActivityMonitor} to be hit till the timeout
+ * expires. Once the monitor has been hit, it is removed from the activity
+ * monitor list and the first created Activity object that matched it is
+ * returned. If the timeout expires, a null object is returned.
+ *
+ * @param monitor The ActivityMonitor to wait for.
+ * @param timeOut The timeout value in secs.
+ *
+ * @return The Activity object that matched the monitor.
+ */
+ public Activity waitForMonitorWithTimeout(ActivityMonitor monitor, long timeOut) {
+ Activity activity = monitor.waitForActivityWithTimeout(timeOut);
+ synchronized (mSync) {
+ mActivityMonitors.remove(monitor);
+ }
+ return activity;
+ }
+
+ /**
+ * Remove an {@link ActivityMonitor} that was previously added with
+ * {@link #addMonitor}.
+ *
+ * @param monitor The monitor to remove.
+ *
+ * @see #addMonitor
+ */
+ public void removeMonitor(ActivityMonitor monitor) {
+ synchronized (mSync) {
+ mActivityMonitors.remove(monitor);
+ }
+ }
+
+ /**
+ * Execute a particular menu item.
+ *
+ * @param targetActivity The activity in question.
+ * @param id The identifier associated with the menu item.
+ * @param flag Additional flags, if any.
+ * @return Whether the invocation was successful (for example, it could be
+ * false if item is disabled).
+ */
+ public boolean invokeMenuActionSync(Activity targetActivity,
+ int id, int flag) {
+ class MenuRunnable implements Runnable {
+ private final Activity activity;
+ private final int identifier;
+ private final int flags;
+ boolean returnValue;
+
+ public MenuRunnable(Activity _activity, int _identifier,
+ int _flags) {
+ activity = _activity;
+ identifier = _identifier;
+ flags = _flags;
+ }
+
+ public void run() {
+ Window win = activity.getWindow();
+
+ returnValue = win.performPanelIdentifierAction(
+ Window.FEATURE_OPTIONS_PANEL,
+ identifier,
+ flags);
+ }
+
+ }
+ MenuRunnable mr = new MenuRunnable(targetActivity, id, flag);
+ runOnMainSync(mr);
+ return mr.returnValue;
+ }
+
+ /**
+ * Show the context menu for the currently focused view and executes a
+ * particular context menu item.
+ *
+ * @param targetActivity The activity in question.
+ * @param id The identifier associated with the context menu item.
+ * @param flag Additional flags, if any.
+ * @return Whether the invocation was successful (for example, it could be
+ * false if item is disabled).
+ */
+ public boolean invokeContextMenuAction(Activity targetActivity, int id, int flag) {
+ validateNotAppThread();
+
+ // Bring up context menu for current focus.
+ // It'd be nice to do this through code, but currently ListView depends on
+ // long press to set metadata for its selected child
+
+ final KeyEvent downEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);
+ sendKeySync(downEvent);
+
+ // Need to wait for long press
+ waitForIdleSync();
+ try {
+ Thread.sleep(ViewConfiguration.getLongPressTimeout());
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Could not sleep for long press timeout", e);
+ return false;
+ }
+
+ final KeyEvent upEvent = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER);
+ sendKeySync(upEvent);
+
+ // Wait for context menu to appear
+ waitForIdleSync();
+
+ class ContextMenuRunnable implements Runnable {
+ private final Activity activity;
+ private final int identifier;
+ private final int flags;
+ boolean returnValue;
+
+ public ContextMenuRunnable(Activity _activity, int _identifier,
+ int _flags) {
+ activity = _activity;
+ identifier = _identifier;
+ flags = _flags;
+ }
+
+ public void run() {
+ Window win = activity.getWindow();
+ returnValue = win.performContextMenuIdentifierAction(
+ identifier,
+ flags);
+ }
+
+ }
+
+ ContextMenuRunnable cmr = new ContextMenuRunnable(targetActivity, id, flag);
+ runOnMainSync(cmr);
+ return cmr.returnValue;
+ }
+
+ /**
+ * Sends the key events corresponding to the text to the app being
+ * instrumented.
+ *
+ * @param text The text to be sent.
+ */
+ public void sendStringSync(String text) {
+ if (text == null) {
+ return;
+ }
+ KeyCharacterMap keyCharacterMap =
+ KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+
+ KeyEvent[] events = keyCharacterMap.getEvents(text.toCharArray());
+
+ if (events != null) {
+ for (int i = 0; i < events.length; i++) {
+ sendKeySync(events[i]);
+ }
+ }
+ }
+
+ /**
+ * Send a key event to the currently focused window/view and wait for it to
+ * be processed. Finished at some point after the recipient has returned
+ * from its event processing, though it may <em>not</em> have completely
+ * finished reacting from the event -- for example, if it needs to update
+ * its display as a result, it may still be in the process of doing that.
+ *
+ * @param event The event to send to the current focus.
+ */
+ public void sendKeySync(KeyEvent event) {
+ validateNotAppThread();
+ try {
+ (IWindowManager.Stub.asInterface(ServiceManager.getService("window")))
+ .injectKeyEvent(event, true);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Sends an up and down key event sync to the currently focused window.
+ *
+ * @param key The integer keycode for the event.
+ */
+ public void sendKeyDownUpSync(int key) {
+ sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, key));
+ sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, key));
+ }
+
+ /**
+ * Higher-level method for sending both the down and up key events for a
+ * particular character key code. Equivalent to creating both KeyEvent
+ * objects by hand and calling {@link #sendKeySync}. The event appears
+ * as if it came from keyboard 0, the built in one.
+ *
+ * @param keyCode The key code of the character to send.
+ */
+ public void sendCharacterSync(int keyCode) {
+ sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+ sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
+ }
+
+ /**
+ * Dispatch a pointer event. Finished at some point after the recipient has
+ * returned from its event processing, though it may <em>not</em> have
+ * completely finished reacting from the event -- for example, if it needs
+ * to update its display as a result, it may still be in the process of
+ * doing that.
+ *
+ * @param event A motion event describing the pointer action. (As noted in
+ * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
+ * {@link SystemClock#uptimeMillis()} as the timebase.
+ */
+ public void sendPointerSync(MotionEvent event) {
+ validateNotAppThread();
+ try {
+ (IWindowManager.Stub.asInterface(ServiceManager.getService("window")))
+ .injectPointerEvent(event, true);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Dispatch a trackball event. Finished at some point after the recipient has
+ * returned from its event processing, though it may <em>not</em> have
+ * completely finished reacting from the event -- for example, if it needs
+ * to update its display as a result, it may still be in the process of
+ * doing that.
+ *
+ * @param event A motion event describing the trackball action. (As noted in
+ * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
+ * {@link SystemClock#uptimeMillis()} as the timebase.
+ */
+ public void sendTrackballEventSync(MotionEvent event) {
+ validateNotAppThread();
+ try {
+ (IWindowManager.Stub.asInterface(ServiceManager.getService("window")))
+ .injectTrackballEvent(event, true);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Perform instantiation of the process's {@link Application} object. The
+ * default implementation provides the normal system behavior.
+ *
+ * @param cl The ClassLoader with which to instantiate the object.
+ * @param className The name of the class implementing the Application
+ * object.
+ * @param context The context to initialize the application with
+ *
+ * @return The newly instantiated Application object.
+ */
+ public Application newApplication(ClassLoader cl, String className, Context context)
+ throws InstantiationException, IllegalAccessException,
+ ClassNotFoundException {
+ return newApplication(cl.loadClass(className), context);
+ }
+
+ /**
+ * Perform instantiation of the process's {@link Application} object. The
+ * default implementation provides the normal system behavior.
+ *
+ * @param clazz The class used to create an Application object from.
+ * @param context The context to initialize the application with
+ *
+ * @return The newly instantiated Application object.
+ */
+ static public Application newApplication(Class<?> clazz, Context context)
+ throws InstantiationException, IllegalAccessException,
+ ClassNotFoundException {
+ Application app = (Application)clazz.newInstance();
+ app.attach(context);
+ return app;
+ }
+
+ /**
+ * Perform calling of the application's {@link Application#onCreate}
+ * method. The default implementation simply calls through to that method.
+ *
+ * @param app The application being created.
+ */
+ public void callApplicationOnCreate(Application app) {
+ app.onCreate();
+ }
+
+ /**
+ * Perform instantiation of an {@link Activity} object. This method is intended for use with
+ * unit tests, such as android.test.ActivityUnitTestCase. The activity will be useable
+ * locally but will be missing some of the linkages necessary for use within the sytem.
+ *
+ * @param clazz The Class of the desired Activity
+ * @param context The base context for the activity to use
+ * @param token The token for this activity to communicate with
+ * @param application The application object (if any)
+ * @param intent The intent that started this Activity
+ * @param info ActivityInfo from the manifest
+ * @param title The title, typically retrieved from the ActivityInfo record
+ * @param parent The parent Activity (if any)
+ * @param id The embedded Id (if any)
+ * @param lastNonConfigurationInstance Arbitrary object that will be
+ * available via {@link Activity#getLastNonConfigurationInstance()
+ * Activity.getLastNonConfigurationInstance()}.
+ * @return Returns the instantiated activity
+ * @throws InstantiationException
+ * @throws IllegalAccessException
+ */
+ public Activity newActivity(Class<?> clazz, Context context,
+ IBinder token, Application application, Intent intent, ActivityInfo info,
+ CharSequence title, Activity parent, String id,
+ Object lastNonConfigurationInstance) throws InstantiationException,
+ IllegalAccessException {
+ Activity activity = (Activity)clazz.newInstance();
+ ActivityThread aThread = null;
+ activity.attach(context, aThread, this, token, application, intent, info, title,
+ parent, id, lastNonConfigurationInstance, new Configuration());
+ return activity;
+ }
+
+ /**
+ * Perform instantiation of the process's {@link Activity} object. The
+ * default implementation provides the normal system behavior.
+ *
+ * @param cl The ClassLoader with which to instantiate the object.
+ * @param className The name of the class implementing the Activity
+ * object.
+ * @param intent The Intent object that specified the activity class being
+ * instantiated.
+ *
+ * @return The newly instantiated Activity object.
+ */
+ public Activity newActivity(ClassLoader cl, String className,
+ Intent intent)
+ throws InstantiationException, IllegalAccessException,
+ ClassNotFoundException {
+ return (Activity)cl.loadClass(className).newInstance();
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onCreate}
+ * method. The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being created.
+ * @param icicle The previously frozen state (or null) to pass through to
+ * onCreate().
+ */
+ public void callActivityOnCreate(Activity activity, Bundle icicle) {
+ if (mWaitingActivities != null) {
+ synchronized (mSync) {
+ final int N = mWaitingActivities.size();
+ for (int i=0; i<N; i++) {
+ final ActivityWaiter aw = mWaitingActivities.get(i);
+ final Intent intent = aw.intent;
+ if (intent.filterEquals(activity.getIntent())) {
+ aw.activity = activity;
+ mMessageQueue.addIdleHandler(new ActivityGoing(aw));
+ }
+ }
+ }
+ }
+
+ activity.onCreate(icicle);
+
+ if (mActivityMonitors != null) {
+ synchronized (mSync) {
+ final int N = mActivityMonitors.size();
+ for (int i=0; i<N; i++) {
+ final ActivityMonitor am = mActivityMonitors.get(i);
+ am.match(activity, activity, activity.getIntent());
+ }
+ }
+ }
+ }
+
+ public void callActivityOnDestroy(Activity activity) {
+ if (mWaitingActivities != null) {
+ synchronized (mSync) {
+ final int N = mWaitingActivities.size();
+ for (int i=0; i<N; i++) {
+ final ActivityWaiter aw = mWaitingActivities.get(i);
+ final Intent intent = aw.intent;
+ if (intent.filterEquals(activity.getIntent())) {
+ aw.activity = activity;
+ mMessageQueue.addIdleHandler(new ActivityGoing(aw));
+ }
+ }
+ }
+ }
+
+ activity.onDestroy();
+
+ if (mActivityMonitors != null) {
+ synchronized (mSync) {
+ final int N = mActivityMonitors.size();
+ for (int i=0; i<N; i++) {
+ final ActivityMonitor am = mActivityMonitors.get(i);
+ am.match(activity, activity, activity.getIntent());
+ }
+ }
+ }
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onRestoreInstanceState}
+ * method. The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being restored.
+ * @param savedInstanceState The previously saved state being restored.
+ */
+ public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
+ activity.performRestoreInstanceState(savedInstanceState);
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onPostCreate} method.
+ * The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being created.
+ * @param icicle The previously frozen state (or null) to pass through to
+ * onPostCreate().
+ */
+ public void callActivityOnPostCreate(Activity activity, Bundle icicle) {
+ activity.onPostCreate(icicle);
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onNewIntent}
+ * method. The default implementation simply calls through to that method.
+ *
+ * @param activity The activity receiving a new Intent.
+ * @param intent The new intent being received.
+ */
+ public void callActivityOnNewIntent(Activity activity, Intent intent) {
+ activity.onNewIntent(intent);
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onStart}
+ * method. The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being started.
+ */
+ public void callActivityOnStart(Activity activity) {
+ activity.onStart();
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onRestart}
+ * method. The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being restarted.
+ */
+ public void callActivityOnRestart(Activity activity) {
+ activity.onRestart();
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onResume} method. The
+ * default implementation simply calls through to that method.
+ *
+ * @param activity The activity being resumed.
+ */
+ public void callActivityOnResume(Activity activity) {
+ activity.onResume();
+
+ if (mActivityMonitors != null) {
+ synchronized (mSync) {
+ final int N = mActivityMonitors.size();
+ for (int i=0; i<N; i++) {
+ final ActivityMonitor am = mActivityMonitors.get(i);
+ am.match(activity, activity, activity.getIntent());
+ }
+ }
+ }
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onStop}
+ * method. The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being stopped.
+ */
+ public void callActivityOnStop(Activity activity) {
+ activity.onStop();
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onPause} method. The
+ * default implementation simply calls through to that method.
+ *
+ * @param activity The activity being saved.
+ * @param outState The bundle to pass to the call.
+ */
+ public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {
+ activity.performSaveInstanceState(outState);
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onPause} method. The
+ * default implementation simply calls through to that method.
+ *
+ * @param activity The activity being paused.
+ */
+ public void callActivityOnPause(Activity activity) {
+ activity.performPause();
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onUserLeaveHint} method.
+ * The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being notified that the user has navigated away
+ */
+ public void callActivityOnUserLeaving(Activity activity) {
+ activity.performUserLeaving();
+ }
+
+ /*
+ * Starts allocation counting. This triggers a gc and resets the counts.
+ */
+ public void startAllocCounting() {
+ // Before we start trigger a GC and reset the debug counts. Run the
+ // finalizers and another GC before starting and stopping the alloc
+ // counts. This will free up any objects that were just sitting around
+ // waiting for their finalizers to be run.
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+ Runtime.getRuntime().gc();
+
+ Debug.resetAllCounts();
+
+ // start the counts
+ Debug.startAllocCounting();
+ }
+
+ /*
+ * Stops allocation counting.
+ */
+ public void stopAllocCounting() {
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+ Runtime.getRuntime().gc();
+ Debug.stopAllocCounting();
+ }
+
+ /**
+ * If Results already contains Key, it appends Value to the key's ArrayList
+ * associated with the key. If the key doesn't already exist in results, it
+ * adds the key/value pair to results.
+ */
+ private void addValue(String key, int value, Bundle results) {
+ if (results.containsKey(key)) {
+ List<Integer> list = results.getIntegerArrayList(key);
+ if (list != null) {
+ list.add(value);
+ }
+ } else {
+ ArrayList<Integer> list = new ArrayList<Integer>();
+ list.add(value);
+ results.putIntegerArrayList(key, list);
+ }
+ }
+
+ /**
+ * Returns a bundle with the current results from the allocation counting.
+ */
+ public Bundle getAllocCounts() {
+ Bundle results = new Bundle();
+ results.putLong("global_alloc_count", Debug.getGlobalAllocCount());
+ results.putLong("global_alloc_size", Debug.getGlobalAllocSize());
+ results.putLong("global_freed_count", Debug.getGlobalFreedCount());
+ results.putLong("global_freed_size", Debug.getGlobalFreedSize());
+ results.putLong("gc_invocation_count", Debug.getGlobalGcInvocationCount());
+ return results;
+ }
+
+ /**
+ * Returns a bundle with the counts for various binder counts for this process. Currently the only two that are
+ * reported are the number of send and the number of received transactions.
+ */
+ public Bundle getBinderCounts() {
+ Bundle results = new Bundle();
+ results.putLong("sent_transactions", Debug.getBinderSentTransactions());
+ results.putLong("received_transactions", Debug.getBinderReceivedTransactions());
+ return results;
+ }
+
+ /**
+ * Description of a Activity execution result to return to the original
+ * activity.
+ */
+ public static final class ActivityResult {
+ /**
+ * Create a new activity result. See {@link Activity#setResult} for
+ * more information.
+ *
+ * @param resultCode The result code to propagate back to the
+ * originating activity, often RESULT_CANCELED or RESULT_OK
+ * @param resultData The data to propagate back to the originating
+ * activity.
+ */
+ public ActivityResult(int resultCode, Intent resultData) {
+ mResultCode = resultCode;
+ mResultData = resultData;
+ }
+
+ /**
+ * Retrieve the result code contained in this result.
+ */
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ /**
+ * Retrieve the data contained in this result.
+ */
+ public Intent getResultData() {
+ return mResultData;
+ }
+
+ private final int mResultCode;
+ private final Intent mResultData;
+ }
+
+ /**
+ * Execute a startActivity call made by the application. The default
+ * implementation takes care of updating any active {@link ActivityMonitor}
+ * objects and dispatches this call to the system activity manager; you can
+ * override this to watch for the application to start an activity, and
+ * modify what happens when it does.
+ *
+ * <p>This method returns an {@link ActivityResult} object, which you can
+ * use when intercepting application calls to avoid performing the start
+ * activity action but still return the result the application is
+ * expecting. To do this, override this method to catch the call to start
+ * activity so that it returns a new ActivityResult containing the results
+ * you would like the application to see, and don't call up to the super
+ * class. Note that an application is only expecting a result if
+ * <var>requestCode</var> is &gt;= 0.
+ *
+ * <p>This method throws {@link android.content.ActivityNotFoundException}
+ * if there was no Activity found to run the given Intent.
+ *
+ * @param who The Context from which the activity is being started.
+ * @param contextThread The main thread of the Context from which the activity
+ * is being started.
+ * @param token Internal token identifying to the system who is starting
+ * the activity; may be null.
+ * @param target Which activity is perform the start (and thus receiving
+ * any result); may be null if this call is not being made
+ * from an activity.
+ * @param intent The actual Intent to start.
+ * @param requestCode Identifier for this request's result; less than zero
+ * if the caller is not expecting a result.
+ *
+ * @return To force the return of a particular result, return an
+ * ActivityResult object containing the desired data; otherwise
+ * return null. The default implementation always returns null.
+ *
+ * @throws android.content.ActivityNotFoundException
+ *
+ * @see Activity#startActivity(Intent)
+ * @see Activity#startActivityForResult(Intent, int)
+ * @see Activity#startActivityFromChild
+ *
+ * {@hide}
+ */
+ public ActivityResult execStartActivity(
+ Context who, IBinder contextThread, IBinder token, Activity target,
+ Intent intent, int requestCode) {
+ IApplicationThread whoThread = (IApplicationThread) contextThread;
+ if (mActivityMonitors != null) {
+ synchronized (mSync) {
+ final int N = mActivityMonitors.size();
+ for (int i=0; i<N; i++) {
+ final ActivityMonitor am = mActivityMonitors.get(i);
+ if (am.match(who, null, intent)) {
+ am.mHits++;
+ if (am.isBlocking()) {
+ return requestCode >= 0 ? am.getResult() : null;
+ }
+ break;
+ }
+ }
+ }
+ }
+ try {
+ int result = ActivityManagerNative.getDefault()
+ .startActivity(whoThread, intent,
+ intent.resolveTypeIfNeeded(who.getContentResolver()),
+ null, 0, token, target != null ? target.mEmbeddedID : null,
+ requestCode, false, false);
+ checkStartActivityResult(result, intent);
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /*package*/ final void init(ActivityThread thread,
+ Context instrContext, Context appContext, ComponentName component,
+ IInstrumentationWatcher watcher) {
+ mThread = thread;
+ mMessageQueue = mThread.getLooper().myQueue();
+ mInstrContext = instrContext;
+ mAppContext = appContext;
+ mComponent = component;
+ mWatcher = watcher;
+ }
+
+ /*package*/ static void checkStartActivityResult(int res, Intent intent) {
+ if (res >= IActivityManager.START_SUCCESS) {
+ return;
+ }
+
+ switch (res) {
+ case IActivityManager.START_INTENT_NOT_RESOLVED:
+ case IActivityManager.START_CLASS_NOT_FOUND:
+ if (intent.getComponent() != null)
+ throw new ActivityNotFoundException(
+ "Unable to find explicit activity class "
+ + intent.getComponent().toShortString()
+ + "; have you declared this activity in your AndroidManifest.xml?");
+ throw new ActivityNotFoundException(
+ "No Activity found to handle " + intent);
+ case IActivityManager.START_PERMISSION_DENIED:
+ throw new SecurityException("Not allowed to start activity "
+ + intent);
+ case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
+ throw new AndroidRuntimeException(
+ "FORWARD_RESULT_FLAG used while also requesting a result");
+ default:
+ throw new AndroidRuntimeException("Unknown error code "
+ + res + " when starting " + intent);
+ }
+ }
+
+ private final void validateNotAppThread() {
+ if (ActivityThread.currentActivityThread() != null) {
+ throw new RuntimeException(
+ "This method can not be called from the main application thread");
+ }
+ }
+
+ private final class InstrumentationThread extends Thread {
+ public InstrumentationThread(String name) {
+ super(name);
+ }
+ public void run() {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ try {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Exception setting priority of instrumentation thread "
+ + Process.myTid(), e);
+ }
+ if (mAutomaticPerformanceSnapshots) {
+ startPerformanceSnapshot();
+ }
+ onStart();
+ }
+ }
+
+ private static final class EmptyRunnable implements Runnable {
+ public void run() {
+ }
+ }
+
+ private static final class SyncRunnable implements Runnable {
+ private final Runnable mTarget;
+ private boolean mComplete;
+
+ public SyncRunnable(Runnable target) {
+ mTarget = target;
+ }
+
+ public void run() {
+ mTarget.run();
+ synchronized (this) {
+ mComplete = true;
+ notifyAll();
+ }
+ }
+
+ public void waitForComplete() {
+ synchronized (this) {
+ while (!mComplete) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+ }
+
+ private static final class ActivityWaiter {
+ public final Intent intent;
+ public Activity activity;
+
+ public ActivityWaiter(Intent _intent) {
+ intent = _intent;
+ }
+ }
+
+ private final class ActivityGoing implements MessageQueue.IdleHandler {
+ private final ActivityWaiter mWaiter;
+
+ public ActivityGoing(ActivityWaiter waiter) {
+ mWaiter = waiter;
+ }
+
+ public final boolean queueIdle() {
+ synchronized (mSync) {
+ mWaitingActivities.remove(mWaiter);
+ mSync.notifyAll();
+ }
+ return false;
+ }
+ }
+
+ private static final class Idler implements MessageQueue.IdleHandler {
+ private final Runnable mCallback;
+ private boolean mIdle;
+
+ public Idler(Runnable callback) {
+ mCallback = callback;
+ mIdle = false;
+ }
+
+ public final boolean queueIdle() {
+ if (mCallback != null) {
+ mCallback.run();
+ }
+ synchronized (this) {
+ mIdle = true;
+ notifyAll();
+ }
+ return false;
+ }
+
+ public void waitForIdle() {
+ synchronized (this) {
+ while (!mIdle) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
new file mode 100644
index 0000000..2b12a2a
--- /dev/null
+++ b/core/java/android/app/IntentService.java
@@ -0,0 +1,74 @@
+package android.app;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * An abstract {@link Service} that serializes the handling of the Intents passed upon service
+ * start and handles them on a handler thread.
+ *
+ * <p>To use this class extend it and implement {@link #onHandleIntent}. The {@link Service} will
+ * automatically be stopped when the last enqueued {@link Intent} is handled.
+ */
+public abstract class IntentService extends Service {
+ private volatile Looper mServiceLooper;
+ private volatile ServiceHandler mServiceHandler;
+ private String mName;
+
+ private final class ServiceHandler extends Handler {
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ onHandleIntent((Intent)msg.obj);
+ stopSelf(msg.arg1);
+ }
+ }
+
+ public IntentService(String name) {
+ super();
+ mName = name;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
+ thread.start();
+
+ mServiceLooper = thread.getLooper();
+ mServiceHandler = new ServiceHandler(mServiceLooper);
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ super.onStart(intent, startId);
+ Message msg = mServiceHandler.obtainMessage();
+ msg.arg1 = startId;
+ msg.obj = intent;
+ mServiceHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onDestroy() {
+ mServiceLooper.quit();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ /**
+ * Invoked on the Handler thread with the {@link Intent} that is passed to {@link #onStart}.
+ * Note that this will be invoked from a different thread than the one that handles the
+ * {@link #onStart} call.
+ */
+ protected abstract void onHandleIntent(Intent intent);
+}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
new file mode 100644
index 0000000..0c07553
--- /dev/null
+++ b/core/java/android/app/KeyguardManager.java
@@ -0,0 +1,155 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.view.IWindowManager;
+import android.view.IOnKeyguardExitResult;
+
+/**
+ * Class that can be used to lock and unlock the keyboard. Get an instance of this
+ * class by calling {@link android.content.Context#getSystemService(java.lang.String)}
+ * with argument {@link android.content.Context#KEYGUARD_SERVICE}. The
+ * Actual class to control the keyboard locking is
+ * {@link android.app.KeyguardManager.KeyguardLock}.
+ */
+public class KeyguardManager {
+ private IWindowManager mWM;
+
+ /**
+ * Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
+ * you to disable / reenable the keyguard.
+ */
+ public class KeyguardLock {
+ private IBinder mToken = new Binder();
+ private String mTag;
+
+ KeyguardLock(String tag) {
+ mTag = tag;
+ }
+
+ /**
+ * Disable the keyguard from showing. If the keyguard is currently
+ * showing, hide it. The keyguard will be prevented from showing again
+ * until {@link #reenableKeyguard()} is called.
+ *
+ * A good place to call this is from {@link android.app.Activity#onResume()}
+ *
+ * @see #reenableKeyguard()
+ */
+ public void disableKeyguard() {
+ try {
+ mWM.disableKeyguard(mToken, mTag);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ /**
+ * Reenable the keyguard. The keyguard will reappear if the previous
+ * call to {@link #disableKeyguard()} caused it it to be hidden.
+ *
+ * A good place to call this is from {@link android.app.Activity#onPause()}
+ *
+ * @see #disableKeyguard()
+ */
+ public void reenableKeyguard() {
+ try {
+ mWM.reenableKeyguard(mToken);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ /**
+ * Callback passed to {@link KeyguardManager#exitKeyguardSecurely} to notify
+ * caller of result.
+ */
+ public interface OnKeyguardExitResult {
+
+ /**
+ * @param success True if the user was able to authenticate, false if
+ * not.
+ */
+ void onKeyguardExitResult(boolean success);
+ }
+
+
+ KeyguardManager() {
+ mWM = IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
+ }
+
+ /**
+ * Enables you to lock or unlock the keyboard. Get an instance of this class by
+ * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
+ * This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
+ * @param tag A tag that informally identifies who you are (for debugging who
+ * is disabling he keyguard).
+ *
+ * @return A {@link KeyguardLock} handle to use to disable and reenable the
+ * keyguard.
+ */
+ public KeyguardLock newKeyguardLock(String tag) {
+ return new KeyguardLock(tag);
+ }
+
+ /**
+ * If keyguard screen is showing or in restricted key input mode (i.e. in
+ * keyguard password emergency screen). When in such mode, certain keys,
+ * such as the Home key and the right soft keys, don't work.
+ *
+ * @return true if in keyguard restricted input mode.
+ *
+ * @see android.view.WindowManagerPolicy#inKeyguardRestrictedKeyInputMode
+ */
+ public boolean inKeyguardRestrictedInputMode() {
+ try {
+ return mWM.inKeyguardRestrictedInputMode();
+ } catch (RemoteException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Exit the keyguard securely. The use case for this api is that, after
+ * disabling the keyguard, your app, which was granted permission to
+ * disable the keyguard and show a limited amount of information deemed
+ * safe without the user getting past the keyguard, needs to navigate to
+ * something that is not safe to view without getting past the keyguard.
+ *
+ * This will, if the keyguard is secure, bring up the unlock screen of
+ * the keyguard.
+ *
+ * @param callback Let's you know whether the operation was succesful and
+ * it is safe to launch anything that would normally be considered safe
+ * once the user has gotten past the keyguard.
+ */
+ public void exitKeyguardSecurely(final OnKeyguardExitResult callback) {
+ try {
+ mWM.exitKeyguardSecurely(new IOnKeyguardExitResult.Stub() {
+ public void onKeyguardExitResult(boolean success) throws RemoteException {
+ callback.onKeyguardExitResult(success);
+ }
+ });
+ } catch (RemoteException e) {
+
+ }
+ }
+}
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
new file mode 100644
index 0000000..d6fcbb1
--- /dev/null
+++ b/core/java/android/app/LauncherActivity.java
@@ -0,0 +1,380 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.PaintDrawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.BaseAdapter;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Displays a list of all activities which can be performed
+ * for a given intent. Launches when clicked.
+ *
+ */
+public abstract class LauncherActivity extends ListActivity {
+
+ Intent mIntent;
+ PackageManager mPackageManager;
+
+ /**
+ * An item in the list
+ */
+ public static class ListItem {
+ public CharSequence label;
+ //public CharSequence description;
+ public Drawable icon;
+ public String packageName;
+ public String className;
+
+ ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) {
+ label = resolveInfo.loadLabel(pm);
+ if (label == null && resolveInfo.activityInfo != null) {
+ label = resolveInfo.activityInfo.name;
+ }
+
+ /*
+ if (resolveInfo.activityInfo != null &&
+ resolveInfo.activityInfo.applicationInfo != null) {
+ description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm);
+ }
+ */
+
+ icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
+ packageName = resolveInfo.activityInfo.applicationInfo.packageName;
+ className = resolveInfo.activityInfo.name;
+ }
+
+ public ListItem() {
+ }
+ }
+
+ /**
+ * Adapter which shows the set of activities that can be performed for a given intent.
+ */
+ private class ActivityAdapter extends BaseAdapter implements Filterable {
+ private final Object lock = new Object();
+ private ArrayList<ListItem> mOriginalValues;
+
+ protected final LayoutInflater mInflater;
+
+ protected List<ListItem> mActivitiesList;
+
+ private Filter mFilter;
+
+ public ActivityAdapter() {
+ mInflater = (LayoutInflater) LauncherActivity.this.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ mActivitiesList = makeListItems();
+ }
+
+ public Intent intentForPosition(int position) {
+ if (mActivitiesList == null) {
+ return null;
+ }
+
+ Intent intent = new Intent(mIntent);
+ ListItem item = mActivitiesList.get(position);
+ intent.setClassName(item.packageName, item.className);
+ return intent;
+ }
+
+ public int getCount() {
+ return mActivitiesList != null ? mActivitiesList.size() : 0;
+ }
+
+ public Object getItem(int position) {
+ return position;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view;
+ if (convertView == null) {
+ view = mInflater.inflate(
+ com.android.internal.R.layout.activity_list_item_2, parent, false);
+ } else {
+ view = convertView;
+ }
+ bindView(view, mActivitiesList.get(position));
+ return view;
+ }
+
+ private void bindView(View view, ListItem item) {
+ TextView text = (TextView) view;
+ text.setText(item.label);
+ text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
+ }
+
+ public Filter getFilter() {
+ if (mFilter == null) {
+ mFilter = new ArrayFilter();
+ }
+ return mFilter;
+ }
+
+ /**
+ * An array filters constrains the content of the array adapter with a prefix. Each
+ * item that does not start with the supplied prefix is removed from the list.
+ */
+ private class ArrayFilter extends Filter {
+ @Override
+ protected FilterResults performFiltering(CharSequence prefix) {
+ FilterResults results = new FilterResults();
+
+ if (mOriginalValues == null) {
+ synchronized (lock) {
+ mOriginalValues = new ArrayList<ListItem>(mActivitiesList);
+ }
+ }
+
+ if (prefix == null || prefix.length() == 0) {
+ synchronized (lock) {
+ ArrayList<ListItem> list = new ArrayList<ListItem>(mOriginalValues);
+ results.values = list;
+ results.count = list.size();
+ }
+ } else {
+ final String prefixString = prefix.toString().toLowerCase();
+
+ ArrayList<ListItem> values = mOriginalValues;
+ int count = values.size();
+
+ ArrayList<ListItem> newValues = new ArrayList<ListItem>(count);
+
+ for (int i = 0; i < count; i++) {
+ ListItem item = values.get(i);
+
+ String[] words = item.label.toString().toLowerCase().split(" ");
+ int wordCount = words.length;
+
+ for (int k = 0; k < wordCount; k++) {
+ final String word = words[k];
+
+ if (word.startsWith(prefixString)) {
+ newValues.add(item);
+ break;
+ }
+ }
+ }
+
+ results.values = newValues;
+ results.count = newValues.size();
+ }
+
+ return results;
+ }
+
+ @Override
+ protected void publishResults(CharSequence constraint, FilterResults results) {
+ //noinspection unchecked
+ mActivitiesList = (List<ListItem>) results.values;
+ if (results.count > 0) {
+ notifyDataSetChanged();
+ } else {
+ notifyDataSetInvalidated();
+ }
+ }
+ }
+ }
+
+ /**
+ * Utility class to resize icons to match default icon size.
+ */
+ public class IconResizer {
+ // Code is borrowed from com.android.launcher.Utilities.
+ private int mIconWidth = -1;
+ private int mIconHeight = -1;
+
+ private final Rect mOldBounds = new Rect();
+ private Canvas mCanvas = new Canvas();
+
+ public IconResizer() {
+ mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
+ Paint.FILTER_BITMAP_FLAG));
+
+ final Resources resources = LauncherActivity.this.getResources();
+ mIconWidth = mIconHeight = (int) resources.getDimension(
+ android.R.dimen.app_icon_size);
+ }
+
+ /**
+ * Returns a Drawable representing the thumbnail of the specified Drawable.
+ * The size of the thumbnail is defined by the dimension
+ * android.R.dimen.launcher_application_icon_size.
+ *
+ * This method is not thread-safe and should be invoked on the UI thread only.
+ *
+ * @param icon The icon to get a thumbnail of.
+ *
+ * @return A thumbnail for the specified icon or the icon itself if the
+ * thumbnail could not be created.
+ */
+ public Drawable createIconThumbnail(Drawable icon) {
+ int width = mIconWidth;
+ int height = mIconHeight;
+
+ final int iconWidth = icon.getIntrinsicWidth();
+ final int iconHeight = icon.getIntrinsicHeight();
+
+ if (icon instanceof PaintDrawable) {
+ PaintDrawable painter = (PaintDrawable) icon;
+ painter.setIntrinsicWidth(width);
+ painter.setIntrinsicHeight(height);
+ }
+
+ if (width > 0 && height > 0) {
+ if (width < iconWidth || height < iconHeight) {
+ final float ratio = (float) iconWidth / iconHeight;
+
+ if (iconWidth > iconHeight) {
+ height = (int) (width / ratio);
+ } else if (iconHeight > iconWidth) {
+ width = (int) (height * ratio);
+ }
+
+ final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ?
+ Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
+ final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c);
+ final Canvas canvas = mCanvas;
+ canvas.setBitmap(thumb);
+ // Copy the old bounds to restore them later
+ // If we were to do oldBounds = icon.getBounds(),
+ // the call to setBounds() that follows would
+ // change the same instance and we would lose the
+ // old bounds
+ mOldBounds.set(icon.getBounds());
+ final int x = (mIconWidth - width) / 2;
+ final int y = (mIconHeight - height) / 2;
+ icon.setBounds(x, y, x + width, y + height);
+ icon.draw(canvas);
+ icon.setBounds(mOldBounds);
+ icon = new BitmapDrawable(thumb);
+ } else if (iconWidth < width && iconHeight < height) {
+ final Bitmap.Config c = Bitmap.Config.ARGB_8888;
+ final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c);
+ final Canvas canvas = mCanvas;
+ canvas.setBitmap(thumb);
+ mOldBounds.set(icon.getBounds());
+ final int x = (width - iconWidth) / 2;
+ final int y = (height - iconHeight) / 2;
+ icon.setBounds(x, y, x + iconWidth, y + iconHeight);
+ icon.draw(canvas);
+ icon.setBounds(mOldBounds);
+ icon = new BitmapDrawable(thumb);
+ }
+ }
+
+ return icon;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mPackageManager = getPackageManager();
+
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ setProgressBarIndeterminateVisibility(true);
+ setContentView(com.android.internal.R.layout.activity_list);
+
+
+ mIntent = new Intent(getTargetIntent());
+ mIntent.setComponent(null);
+ mAdapter = new ActivityAdapter();
+
+ setListAdapter(mAdapter);
+ getListView().setTextFilterEnabled(true);
+
+ setProgressBarIndeterminateVisibility(false);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ Intent intent = ((ActivityAdapter)mAdapter).intentForPosition(position);
+
+ startActivity(intent);
+ }
+
+ /**
+ * Return the actual Intent for a specific position in our
+ * {@link android.widget.ListView}.
+ * @param position The item whose Intent to return
+ */
+ protected Intent intentForPosition(int position) {
+ ActivityAdapter adapter = (ActivityAdapter) mAdapter;
+ return adapter.intentForPosition(position);
+ }
+
+ /**
+ * Get the base intent to use when running
+ * {@link PackageManager#queryIntentActivities(Intent, int)}.
+ */
+ protected Intent getTargetIntent() {
+ return new Intent();
+ }
+
+ /**
+ * Perform the query to determine which results to show and return a list of them.
+ */
+ public List<ListItem> makeListItems() {
+ // Load all matching activities and sort correctly
+ List<ResolveInfo> list = mPackageManager.queryIntentActivities(mIntent,
+ /* no flags */ 0);
+ Collections.sort(list, new ResolveInfo.DisplayNameComparator(mPackageManager));
+
+ IconResizer resizer = new IconResizer();
+
+ ArrayList<ListItem> result = new ArrayList<ListItem>(list.size());
+ int listSize = list.size();
+ for (int i = 0; i < listSize; i++) {
+ ResolveInfo resolveInfo = list.get(i);
+ result.add(new ListItem(mPackageManager, resolveInfo, resizer));
+ }
+
+ return result;
+ }
+}
diff --git a/core/java/android/app/ListActivity.java b/core/java/android/app/ListActivity.java
new file mode 100644
index 0000000..5523c18
--- /dev/null
+++ b/core/java/android/app/ListActivity.java
@@ -0,0 +1,316 @@
+/*
+ * 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.app;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.Adapter;
+import android.widget.AdapterView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+/**
+ * An activity that displays a list of items by binding to a data source such as
+ * an array or Cursor, and exposes event handlers when the user selects an item.
+ * <p>
+ * ListActivity hosts a {@link android.widget.ListView ListView} object that can
+ * be bound to different data sources, typically either an array or a Cursor
+ * holding query results. Binding, screen layout, and row layout are discussed
+ * in the following sections.
+ * <p>
+ * <strong>Screen Layout</strong>
+ * </p>
+ * <p>
+ * ListActivity has a default layout that consists of a single, full-screen list
+ * in the center of the screen. However, if you desire, you can customize the
+ * screen layout by setting your own view layout with setContentView() in
+ * onCreate(). To do this, your own view MUST contain a ListView object with the
+ * id "@android:id/list" (or {@link android.R.id#list} if it's in code)
+ * <p>
+ * Optionally, your custom view can contain another view object of any type to
+ * display when the list view is empty. This "empty list" notifier must have an
+ * id "android:empty". Note that when an empty view is present, the list view
+ * will be hidden when there is no data to display.
+ * <p>
+ * The following code demonstrates an (ugly) custom screen layout. It has a list
+ * with a green background, and an alternate red "no data" message.
+ * </p>
+ *
+ * <pre>
+ * &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+ * &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:orientation=&quot;vertical&quot;
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;fill_parent&quot;
+ * android:paddingLeft=&quot;8dp&quot;
+ * android:paddingRight=&quot;8dp&quot;&gt;
+ *
+ * &lt;ListView android:id=&quot;@id/android:list&quot;
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;fill_parent&quot;
+ * android:background=&quot;#00FF00&quot;
+ * android:layout_weight=&quot;1&quot;
+ * android:drawSelectorOnTop=&quot;false&quot;/&gt;
+ *
+ * &lt;TextView id=&quot;@id/android:empty&quot;
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;fill_parent&quot;
+ * android:background=&quot;#FF0000&quot;
+ * android:text=&quot;No data&quot;/&gt;
+ * &lt;/LinearLayout&gt;
+ * </pre>
+ *
+ * <p>
+ * <strong>Row Layout</strong>
+ * </p>
+ * <p>
+ * You can specify the layout of individual rows in the list. You do this by
+ * specifying a layout resource in the ListAdapter object hosted by the activity
+ * (the ListAdapter binds the ListView to the data; more on this later).
+ * <p>
+ * A ListAdapter constructor takes a parameter that specifies a layout resource
+ * for each row. It also has two additional parameters that let you specify
+ * which data field to associate with which object in the row layout resource.
+ * These two parameters are typically parallel arrays.
+ * </p>
+ * <p>
+ * Android provides some standard row layout resources. These are in the
+ * {@link android.R.layout} class, and have names such as simple_list_item_1,
+ * simple_list_item_2, and two_line_list_item. The following layout XML is the
+ * source for the resource two_line_list_item, which displays two data
+ * fields,one above the other, for each list row.
+ * </p>
+ *
+ * <pre>
+ * &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+ * &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;wrap_content&quot;
+ * android:orientation=&quot;vertical&quot;&gt;
+ *
+ * &lt;TextView android:id=&quot;@+id/text1&quot;
+ * android:textSize=&quot;16sp&quot;
+ * android:textStyle=&quot;bold&quot;
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;wrap_content&quot;/&gt;
+ *
+ * &lt;TextView android:id=&quot;@+id/text2&quot;
+ * android:textSize=&quot;16sp&quot;
+ * android:layout_width=&quot;fill_parent&quot;
+ * android:layout_height=&quot;wrap_content&quot;/&gt;
+ * &lt;/LinearLayout&gt;
+ * </pre>
+ *
+ * <p>
+ * You must identify the data bound to each TextView object in this layout. The
+ * syntax for this is discussed in the next section.
+ * </p>
+ * <p>
+ * <strong>Binding to Data</strong>
+ * </p>
+ * <p>
+ * You bind the ListActivity's ListView object to data using a class that
+ * implements the {@link android.widget.ListAdapter ListAdapter} interface.
+ * Android provides two standard list adapters:
+ * {@link android.widget.SimpleAdapter SimpleAdapter} for static data (Maps),
+ * and {@link android.widget.SimpleCursorAdapter SimpleCursorAdapter} for Cursor
+ * query results.
+ * </p>
+ * <p>
+ * The following code from a custom ListActivity demonstrates querying the
+ * Contacts provider for all contacts, then binding the Name and Company fields
+ * to a two line row layout in the activity's ListView.
+ * </p>
+ *
+ * <pre>
+ * public class MyListAdapter extends ListActivity {
+ *
+ * &#064;Override
+ * protected void onCreate(Bundle savedInstanceState){
+ * super.onCreate(savedInstanceState);
+ *
+ * // We'll define a custom screen layout here (the one shown above), but
+ * // typically, you could just use the standard ListActivity layout.
+ * setContentView(R.layout.custom_list_activity_view);
+ *
+ * // Query for all people contacts using the {@link android.provider.Contacts.People} convenience class.
+ * // Put a managed wrapper around the retrieved cursor so we don't have to worry about
+ * // requerying or closing it as the activity changes state.
+ * mCursor = People.query(this.getContentResolver(), null);
+ * startManagingCursor(mCursor);
+ *
+ * // Now create a new list adapter bound to the cursor.
+ * // SimpleListAdapter is designed for binding to a Cursor.
+ * ListAdapter adapter = new SimpleCursorAdapter(
+ * this, // Context.
+ * android.R.layout.two_line_list_item, // Specify the row template to use (here, two columns bound to the two retrieved cursor
+ * rows).
+ * mCursor, // Pass in the cursor to bind to.
+ * new String[] {People.NAME, People.COMPANY}, // Array of cursor columns to bind to.
+ * new int[]); // Parallel array of which template objects to bind to those columns.
+ *
+ * // Bind to our new adapter.
+ * setListAdapter(adapter);
+ * }
+ * }
+ * </pre>
+ *
+ * @see #setListAdapter
+ * @see android.widget.ListView
+ */
+public class ListActivity extends Activity {
+ /**
+ * This field should be made private, so it is hidden from the SDK.
+ * {@hide}
+ */
+ protected ListAdapter mAdapter;
+ /**
+ * This field should be made private, so it is hidden from the SDK.
+ * {@hide}
+ */
+ protected ListView mList;
+
+ private Handler mHandler = new Handler();
+ private boolean mFinishedStart = false;
+
+ private Runnable mRequestFocus = new Runnable() {
+ public void run() {
+ mList.focusableViewAvailable(mList);
+ }
+ };
+
+ /**
+ * This method will be called when an item in the list is selected.
+ * Subclasses should override. Subclasses can call
+ * getListView().getItemAtPosition(position) if they need to access the
+ * data associated with the selected item.
+ *
+ * @param l The ListView where the click happened
+ * @param v The view that was clicked within the ListView
+ * @param position The position of the view in the list
+ * @param id The row id of the item that was clicked
+ */
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ }
+
+ /**
+ * Ensures the list view has been created before Activity restores all
+ * of the view states.
+ *
+ *@see Activity#onRestoreInstanceState(Bundle)
+ */
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ ensureList();
+ super.onRestoreInstanceState(state);
+ }
+
+ /**
+ * Updates the screen state (current list and other views) when the
+ * content changes.
+ *
+ * @see Activity#onContentChanged()
+ */
+ @Override
+ public void onContentChanged() {
+ super.onContentChanged();
+ View emptyView = findViewById(com.android.internal.R.id.empty);
+ mList = (ListView)findViewById(com.android.internal.R.id.list);
+ if (mList == null) {
+ throw new RuntimeException(
+ "Your content must have a ListView whose id attribute is " +
+ "'android.R.id.list'");
+ }
+ if (emptyView != null) {
+ mList.setEmptyView(emptyView);
+ }
+ mList.setOnItemClickListener(mOnClickListener);
+ if (mFinishedStart) {
+ setListAdapter(mAdapter);
+ }
+ mHandler.post(mRequestFocus);
+ mFinishedStart = true;
+ }
+
+ /**
+ * Provide the cursor for the list view.
+ */
+ public void setListAdapter(ListAdapter adapter) {
+ synchronized (this) {
+ ensureList();
+ mAdapter = adapter;
+ mList.setAdapter(adapter);
+ }
+ }
+
+ /**
+ * Set the currently selected list item to the specified
+ * position with the adapter's data
+ *
+ * @param position
+ */
+ public void setSelection(int position) {
+ mList.setSelection(position);
+ }
+
+ /**
+ * Get the position of the currently selected list item.
+ */
+ public int getSelectedItemPosition() {
+ return mList.getSelectedItemPosition();
+ }
+
+ /**
+ * Get the cursor row ID of the currently selected list item.
+ */
+ public long getSelectedItemId() {
+ return mList.getSelectedItemId();
+ }
+
+ /**
+ * Get the activity's list view widget.
+ */
+ public ListView getListView() {
+ ensureList();
+ return mList;
+ }
+
+ /**
+ * Get the ListAdapter associated with this activity's ListView.
+ */
+ public ListAdapter getListAdapter() {
+ return mAdapter;
+ }
+
+ private void ensureList() {
+ if (mList != null) {
+ return;
+ }
+ setContentView(com.android.internal.R.layout.list_content);
+
+ }
+
+ private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView parent, View v, int position, long id)
+ {
+ onListItemClick((ListView)parent, v, position, id);
+ }
+ };
+}
+
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
new file mode 100644
index 0000000..a24fcae
--- /dev/null
+++ b/core/java/android/app/LocalActivityManager.java
@@ -0,0 +1,627 @@
+/*
+ * 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.app;
+
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.util.Config;
+import android.util.Log;
+import android.view.Window;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Helper class for managing multiple running embedded activities in the same
+ * process. This class is not normally used directly, but rather created for
+ * you as part of the {@link android.app.ActivityGroup} implementation.
+ *
+ * @see ActivityGroup
+ */
+public class LocalActivityManager {
+ private static final String TAG = "LocalActivityManager";
+ private static final boolean localLOGV = false || Config.LOGV;
+
+ // Internal token for an Activity being managed by LocalActivityManager.
+ private static class LocalActivityRecord extends Binder {
+ LocalActivityRecord(String _id, Intent _intent) {
+ id = _id;
+ intent = _intent;
+ }
+
+ final String id; // Unique name of this record.
+ Intent intent; // Which activity to run here.
+ ActivityInfo activityInfo; // Package manager info about activity.
+ Activity activity; // Currently instantiated activity.
+ Window window; // Activity's top-level window.
+ Bundle instanceState; // Last retrieved freeze state.
+ int curState = RESTORED; // Current state the activity is in.
+ }
+
+ static final int RESTORED = 0; // State restored, but no startActivity().
+ static final int INITIALIZING = 1; // Ready to launch (after startActivity()).
+ static final int CREATED = 2; // Created, not started or resumed.
+ static final int STARTED = 3; // Created and started, not resumed.
+ static final int RESUMED = 4; // Created started and resumed.
+ static final int DESTROYED = 5; // No longer with us.
+
+ /** Thread our activities are running in. */
+ private final ActivityThread mActivityThread;
+ /** The containing activity that owns the activities we create. */
+ private final Activity mParent;
+
+ /** The activity that is currently resumed. */
+ private LocalActivityRecord mResumed;
+ /** id -> record of all known activities. */
+ private final Map<String, LocalActivityRecord> mActivities
+ = new HashMap<String, LocalActivityRecord>();
+ /** array of all known activities for easy iterating. */
+ private final ArrayList<LocalActivityRecord> mActivityArray
+ = new ArrayList<LocalActivityRecord>();
+
+ /** True if only one activity can be resumed at a time */
+ private boolean mSingleMode;
+
+ /** Set to true once we find out the container is finishing. */
+ private boolean mFinishing;
+
+ /** Current state the owner (ActivityGroup) is in */
+ private int mCurState = INITIALIZING;
+
+ /** String ids of running activities starting with least recently used. */
+ // TODO: put back in stopping of activities.
+ //private List<LocalActivityRecord> mLRU = new ArrayList();
+
+ /**
+ * Create a new LocalActivityManager for holding activities running within
+ * the given <var>parent</var>.
+ *
+ * @param parent the host of the embedded activities
+ * @param singleMode True if the LocalActivityManger should keep a maximum
+ * of one activity resumed
+ */
+ public LocalActivityManager(Activity parent, boolean singleMode) {
+ mActivityThread = ActivityThread.currentActivityThread();
+ mParent = parent;
+ mSingleMode = singleMode;
+ }
+
+ private void moveToState(LocalActivityRecord r, int desiredState) {
+ if (r.curState == RESTORED || r.curState == DESTROYED) {
+ // startActivity() has not yet been called, so nothing to do.
+ return;
+ }
+
+ if (r.curState == INITIALIZING) {
+ // Get the lastNonConfigurationInstance for the activity
+ HashMap<String,Object> lastNonConfigurationInstances =
+ mParent.getLastNonConfigurationChildInstances();
+ Object instance = null;
+ if (lastNonConfigurationInstances != null) {
+ instance = lastNonConfigurationInstances.get(r.id);
+ }
+
+ // We need to have always created the activity.
+ if (localLOGV) Log.v(TAG, r.id + ": starting " + r.intent);
+ if (r.activityInfo == null) {
+ r.activityInfo = mActivityThread.resolveActivityInfo(r.intent);
+ }
+ r.activity = mActivityThread.startActivityNow(
+ mParent, r.id, r.intent, r.activityInfo, r, r.instanceState, instance);
+ if (r.activity == null) {
+ return;
+ }
+ r.window = r.activity.getWindow();
+ r.instanceState = null;
+ r.curState = STARTED;
+
+ if (desiredState == RESUMED) {
+ if (localLOGV) Log.v(TAG, r.id + ": resuming");
+ mActivityThread.performResumeActivity(r, true);
+ r.curState = RESUMED;
+ }
+
+ // Don't do anything more here. There is an important case:
+ // if this is being done as part of onCreate() of the group, then
+ // the launching of the activity gets its state a little ahead
+ // of our own (it is now STARTED, while we are only CREATED).
+ // If we just leave things as-is, we'll deal with it as the
+ // group's state catches up.
+ return;
+ }
+
+ switch (r.curState) {
+ case CREATED:
+ if (desiredState == STARTED) {
+ if (localLOGV) Log.v(TAG, r.id + ": restarting");
+ mActivityThread.performRestartActivity(r);
+ r.curState = STARTED;
+ }
+ if (desiredState == RESUMED) {
+ if (localLOGV) Log.v(TAG, r.id + ": restarting and resuming");
+ mActivityThread.performRestartActivity(r);
+ mActivityThread.performResumeActivity(r, true);
+ r.curState = RESUMED;
+ }
+ return;
+
+ case STARTED:
+ if (desiredState == RESUMED) {
+ // Need to resume it...
+ if (localLOGV) Log.v(TAG, r.id + ": resuming");
+ mActivityThread.performResumeActivity(r, true);
+ r.instanceState = null;
+ r.curState = RESUMED;
+ }
+ if (desiredState == CREATED) {
+ if (localLOGV) Log.v(TAG, r.id + ": stopping");
+ mActivityThread.performStopActivity(r);
+ r.curState = CREATED;
+ }
+ return;
+
+ case RESUMED:
+ if (desiredState == STARTED) {
+ if (localLOGV) Log.v(TAG, r.id + ": pausing");
+ performPause(r, mFinishing);
+ r.curState = STARTED;
+ }
+ if (desiredState == CREATED) {
+ if (localLOGV) Log.v(TAG, r.id + ": pausing");
+ performPause(r, mFinishing);
+ if (localLOGV) Log.v(TAG, r.id + ": stopping");
+ mActivityThread.performStopActivity(r);
+ r.curState = CREATED;
+ }
+ return;
+ }
+ }
+
+ private void performPause(LocalActivityRecord r, boolean finishing) {
+ boolean needState = r.instanceState == null;
+ Bundle instanceState = mActivityThread.performPauseActivity(r,
+ finishing, needState);
+ if (needState) {
+ r.instanceState = instanceState;
+ }
+ }
+
+ /**
+ * Start a new activity running in the group. Every activity you start
+ * must have a unique string ID associated with it -- this is used to keep
+ * track of the activity, so that if you later call startActivity() again
+ * on it the same activity object will be retained.
+ *
+ * <p>When there had previously been an activity started under this id,
+ * it may either be destroyed and a new one started, or the current
+ * one re-used, based on these conditions, in order:</p>
+ *
+ * <ul>
+ * <li> If the Intent maps to a different activity component than is
+ * currently running, the current activity is finished and a new one
+ * started.
+ * <li> If the current activity uses a non-multiple launch mode (such
+ * as singleTop), or the Intent has the
+ * {@link Intent#FLAG_ACTIVITY_SINGLE_TOP} flag set, then the current
+ * activity will remain running and its
+ * {@link Activity#onNewIntent(Intent) Activity.onNewIntent()} method
+ * called.
+ * <li> If the new Intent is the same (excluding extras) as the previous
+ * one, and the new Intent does not have the
+ * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} set, then the current activity
+ * will remain running as-is.
+ * <li> Otherwise, the current activity will be finished and a new
+ * one started.
+ * </ul>
+ *
+ * <p>If the given Intent can not be resolved to an available Activity,
+ * this method throws {@link android.content.ActivityNotFoundException}.
+ *
+ * <p>Warning: There is an issue where, if the Intent does not
+ * include an explicit component, we can restore the state for a different
+ * activity class than was previously running when the state was saved (if
+ * the set of available activities changes between those points).
+ *
+ * @param id Unique identifier of the activity to be started
+ * @param intent The Intent describing the activity to be started
+ *
+ * @return Returns the window of the activity. The caller needs to take
+ * care of adding this window to a view hierarchy, and likewise dealing
+ * with removing the old window if the activity has changed.
+ *
+ * @throws android.content.ActivityNotFoundException
+ */
+ public Window startActivity(String id, Intent intent) {
+ if (mCurState == INITIALIZING) {
+ throw new IllegalStateException(
+ "Activities can't be added until the containing group has been created.");
+ }
+
+ boolean adding = false;
+ boolean sameIntent = false;
+
+ ActivityInfo aInfo = null;
+
+ // Already have information about the new activity id?
+ LocalActivityRecord r = mActivities.get(id);
+ if (r == null) {
+ // Need to create it...
+ r = new LocalActivityRecord(id, intent);
+ adding = true;
+ } else if (r.intent != null) {
+ sameIntent = r.intent.filterEquals(intent);
+ if (sameIntent) {
+ // We are starting the same activity.
+ aInfo = r.activityInfo;
+ }
+ }
+ if (aInfo == null) {
+ aInfo = mActivityThread.resolveActivityInfo(intent);
+ }
+
+ // Pause the currently running activity if there is one and only a single
+ // activity is allowed to be running at a time.
+ if (mSingleMode) {
+ LocalActivityRecord old = mResumed;
+
+ // If there was a previous activity, and it is not the current
+ // activity, we need to stop it.
+ if (old != null && old != r && mCurState == RESUMED) {
+ moveToState(old, STARTED);
+ }
+ }
+
+ if (adding) {
+ // It's a brand new world.
+ mActivities.put(id, r);
+ mActivityArray.add(r);
+ } else if (r.activityInfo != null) {
+ // If the new activity is the same as the current one, then
+ // we may be able to reuse it.
+ if (aInfo == r.activityInfo ||
+ (aInfo.name.equals(r.activityInfo.name) &&
+ aInfo.packageName.equals(r.activityInfo.packageName))) {
+ if (aInfo.launchMode != ActivityInfo.LAUNCH_MULTIPLE ||
+ (intent.getFlags()&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0) {
+ // The activity wants onNewIntent() called.
+ ArrayList<Intent> intents = new ArrayList<Intent>(1);
+ intents.add(intent);
+ if (localLOGV) Log.v(TAG, r.id + ": new intent");
+ mActivityThread.performNewIntents(r, intents);
+ r.intent = intent;
+ moveToState(r, mCurState);
+ if (mSingleMode) {
+ mResumed = r;
+ }
+ return r.window;
+ }
+ if (sameIntent &&
+ (intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_TOP) == 0) {
+ // We are showing the same thing, so this activity is
+ // just resumed and stays as-is.
+ r.intent = intent;
+ moveToState(r, mCurState);
+ if (mSingleMode) {
+ mResumed = r;
+ }
+ return r.window;
+ }
+ }
+
+ // The new activity is different than the current one, or it
+ // is a multiple launch activity, so we need to destroy what
+ // is currently there.
+ performDestroy(r, true);
+ }
+
+ r.intent = intent;
+ r.curState = INITIALIZING;
+ r.activityInfo = aInfo;
+
+ moveToState(r, mCurState);
+
+ // When in single mode keep track of the current activity
+ if (mSingleMode) {
+ mResumed = r;
+ }
+ return r.window;
+ }
+
+ private Window performDestroy(LocalActivityRecord r, boolean finish) {
+ Window win = null;
+ win = r.window;
+ if (r.curState == RESUMED && !finish) {
+ performPause(r, finish);
+ }
+ if (localLOGV) Log.v(TAG, r.id + ": destroying");
+ mActivityThread.performDestroyActivity(r, finish);
+ r.activity = null;
+ r.window = null;
+ if (finish) {
+ r.instanceState = null;
+ }
+ r.curState = DESTROYED;
+ return win;
+ }
+
+ /**
+ * Destroy the activity associated with a particular id. This activity
+ * will go through the normal lifecycle events and fine onDestroy(), and
+ * then the id removed from the group.
+ *
+ * @param id Unique identifier of the activity to be destroyed
+ * @param finish If true, this activity will be finished, so its id and
+ * all state are removed from the group.
+ *
+ * @return Returns the window that was used to display the activity, or
+ * null if there was none.
+ */
+ public Window destroyActivity(String id, boolean finish) {
+ LocalActivityRecord r = mActivities.get(id);
+ Window win = null;
+ if (r != null) {
+ win = performDestroy(r, finish);
+ if (finish) {
+ mActivities.remove(r);
+ }
+ }
+ return win;
+ }
+
+ /**
+ * Retrieve the Activity that is currently running.
+ *
+ * @return the currently running (resumed) Activity, or null if there is
+ * not one
+ *
+ * @see #startActivity
+ * @see #getCurrentId
+ */
+ public Activity getCurrentActivity() {
+ return mResumed != null ? mResumed.activity : null;
+ }
+
+ /**
+ * Retrieve the ID of the activity that is currently running.
+ *
+ * @return the ID of the currently running (resumed) Activity, or null if
+ * there is not one
+ *
+ * @see #startActivity
+ * @see #getCurrentActivity
+ */
+ public String getCurrentId() {
+ return mResumed != null ? mResumed.id : null;
+ }
+
+ /**
+ * Return the Activity object associated with a string ID.
+ *
+ * @see #startActivity
+ *
+ * @return the associated Activity object, or null if the id is unknown or
+ * its activity is not currently instantiated
+ */
+ public Activity getActivity(String id) {
+ LocalActivityRecord r = mActivities.get(id);
+ return r != null ? r.activity : null;
+ }
+
+ /**
+ * Restore a state that was previously returned by {@link #saveInstanceState}. This
+ * adds to the activity group information about all activity IDs that had
+ * previously been saved, even if they have not been started yet, so if the
+ * user later navigates to them the correct state will be restored.
+ *
+ * <p>Note: This does <b>not</b> change the current running activity, or
+ * start whatever activity was previously running when the state was saved.
+ * That is up to the client to do, in whatever way it thinks is best.
+ *
+ * @param state a previously saved state; does nothing if this is null
+ *
+ * @see #saveInstanceState
+ */
+ public void dispatchCreate(Bundle state) {
+ if (state != null) {
+ final Iterator<String> i = state.keySet().iterator();
+ while (i.hasNext()) {
+ try {
+ final String id = i.next();
+ final Bundle astate = state.getBundle(id);
+ LocalActivityRecord r = mActivities.get(id);
+ if (r != null) {
+ r.instanceState = astate;
+ } else {
+ r = new LocalActivityRecord(id, null);
+ r.instanceState = astate;
+ mActivities.put(id, r);
+ mActivityArray.add(r);
+ }
+ } catch (Exception e) {
+ // Recover from -all- app errors.
+ Log.e(TAG,
+ "Exception thrown when restoring LocalActivityManager state",
+ e);
+ }
+ }
+ }
+
+ mCurState = CREATED;
+ }
+
+ /**
+ * Retrieve the state of all activities known by the group. For
+ * activities that have previously run and are now stopped or finished, the
+ * last saved state is used. For the current running activity, its
+ * {@link Activity#onSaveInstanceState} is called to retrieve its current state.
+ *
+ * @return a Bundle holding the newly created state of all known activities
+ *
+ * @see #dispatchCreate
+ */
+ public Bundle saveInstanceState() {
+ Bundle state = null;
+
+ // FIXME: child activities will freeze as part of onPaused. Do we
+ // need to do this here?
+ final int N = mActivityArray.size();
+ for (int i=0; i<N; i++) {
+ final LocalActivityRecord r = mActivityArray.get(i);
+ if (state == null) {
+ state = new Bundle();
+ }
+ if ((r.instanceState != null || r.curState == RESUMED)
+ && r.activity != null) {
+ // We need to save the state now, if we don't currently
+ // already have it or the activity is currently resumed.
+ final Bundle childState = new Bundle();
+ r.activity.onSaveInstanceState(childState);
+ r.instanceState = childState;
+ }
+ if (r.instanceState != null) {
+ state.putBundle(r.id, r.instanceState);
+ }
+ }
+
+ return state;
+ }
+
+ /**
+ * Called by the container activity in its {@link Activity#onResume} so
+ * that LocalActivityManager can perform the corresponding action on the
+ * activities it holds.
+ *
+ * @see Activity#onResume
+ */
+ public void dispatchResume() {
+ mCurState = RESUMED;
+ if (mSingleMode) {
+ if (mResumed != null) {
+ moveToState(mResumed, RESUMED);
+ }
+ } else {
+ final int N = mActivityArray.size();
+ for (int i=0; i<N; i++) {
+ moveToState(mActivityArray.get(i), RESUMED);
+ }
+ }
+ }
+
+ /**
+ * Called by the container activity in its {@link Activity#onPause} so
+ * that LocalActivityManager can perform the corresponding action on the
+ * activities it holds.
+ *
+ * @param finishing set to true if the parent activity has been finished;
+ * this can be determined by calling
+ * Activity.isFinishing()
+ *
+ * @see Activity#onPause
+ * @see Activity#isFinishing
+ */
+ public void dispatchPause(boolean finishing) {
+ if (finishing) {
+ mFinishing = true;
+ }
+ mCurState = STARTED;
+ if (mSingleMode) {
+ if (mResumed != null) {
+ moveToState(mResumed, STARTED);
+ }
+ } else {
+ final int N = mActivityArray.size();
+ for (int i=0; i<N; i++) {
+ LocalActivityRecord r = mActivityArray.get(i);
+ if (r.curState == RESUMED) {
+ moveToState(r, STARTED);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called by the container activity in its {@link Activity#onStop} so
+ * that LocalActivityManager can perform the corresponding action on the
+ * activities it holds.
+ *
+ * @see Activity#onStop
+ */
+ public void dispatchStop() {
+ mCurState = CREATED;
+ final int N = mActivityArray.size();
+ for (int i=0; i<N; i++) {
+ LocalActivityRecord r = mActivityArray.get(i);
+ moveToState(r, CREATED);
+ }
+ }
+
+ /**
+ * Call onRetainNonConfigurationInstance on each child activity and store the
+ * results in a HashMap by id. Only construct the HashMap if there is a non-null
+ * object to store. Note that this does not support nested ActivityGroups.
+ *
+ * {@hide}
+ */
+ public HashMap<String,Object> dispatchRetainNonConfigurationInstance() {
+ HashMap<String,Object> instanceMap = null;
+
+ final int N = mActivityArray.size();
+ for (int i=0; i<N; i++) {
+ LocalActivityRecord r = mActivityArray.get(i);
+ if ((r != null) && (r.activity != null)) {
+ Object instance = r.activity.onRetainNonConfigurationInstance();
+ if (instance != null) {
+ if (instanceMap == null) {
+ instanceMap = new HashMap<String,Object>();
+ }
+ instanceMap.put(r.id, instance);
+ }
+ }
+ }
+ return instanceMap;
+ }
+
+ /**
+ * Remove all activities from this LocalActivityManager, performing an
+ * {@link Activity#onDestroy} on any that are currently instantiated.
+ */
+ public void removeAllActivities() {
+ dispatchDestroy(true);
+ }
+
+ /**
+ * Called by the container activity in its {@link Activity#onDestroy} so
+ * that LocalActivityManager can perform the corresponding action on the
+ * activities it holds.
+ *
+ * @see Activity#onDestroy
+ */
+ public void dispatchDestroy(boolean finishing) {
+ final int N = mActivityArray.size();
+ for (int i=0; i<N; i++) {
+ LocalActivityRecord r = mActivityArray.get(i);
+ if (localLOGV) Log.v(TAG, r.id + ": destroying");
+ mActivityThread.performDestroyActivity(r, finishing);
+ }
+ mActivities.clear();
+ mActivityArray.clear();
+ }
+}
diff --git a/core/java/android/app/Notification.aidl b/core/java/android/app/Notification.aidl
new file mode 100644
index 0000000..9d8129c
--- /dev/null
+++ b/core/java/android/app/Notification.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app;
+
+parcelable Notification;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
new file mode 100644
index 0000000..51fddb1
--- /dev/null
+++ b/core/java/android/app/Notification.java
@@ -0,0 +1,483 @@
+/*
+ * 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.app;
+
+import java.util.Date;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.widget.RemoteViews;
+
+/**
+ * A class that represents how a persistent notification is to be presented to
+ * the user using the {@link android.app.NotificationManager}.
+ *
+ */
+public class Notification implements Parcelable
+{
+ /**
+ * Use all default values (where applicable).
+ */
+ public static final int DEFAULT_ALL = ~0;
+
+ /**
+ * Use the default notification sound. This will ignore any given
+ * {@link #sound}.
+ *
+ * @see #defaults
+ */
+ public static final int DEFAULT_SOUND = 1;
+
+ /**
+ * Use the default notification vibrate. This will ignore any given
+ * {@link #vibrate}.
+ *
+ * @see #defaults
+ */
+ public static final int DEFAULT_VIBRATE = 2;
+
+ /**
+ * Use the default notification lights. This will ignore the
+ * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
+ * {@link #ledOnMS}.
+ *
+ * @see #defaults
+ */
+ public static final int DEFAULT_LIGHTS = 4;
+
+ /**
+ * The timestamp for the notification. The icons and expanded views
+ * are sorted by this key.
+ */
+ public long when;
+
+ /**
+ * The resource id of a drawable to use as the icon in the status bar.
+ */
+ public int icon;
+
+ /**
+ * The number of events that this notification represents. For example, if this is the
+ * new mail notification, this would be the number of unread messages. This number is
+ * be superimposed over the icon in the status bar. If the number is 0 or negative, it
+ * is not shown in the status bar.
+ */
+ public int number;
+
+ /**
+ * The intent to execute when the expanded status entry is clicked. If
+ * this is an activity, it must include the
+ * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
+ * that you take care of task management as described in the <em>Activities and Tasks</em>
+ * section of the <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application
+ * Fundamentals</a> document.
+ */
+ public PendingIntent contentIntent;
+
+ /**
+ * The intent to execute when the status entry is deleted by the user
+ * with the "Clear All Notifications" button. This probably shouldn't
+ * be launching an activity since several of those will be sent at the
+ * same time.
+ */
+ public PendingIntent deleteIntent;
+
+ /**
+ * Text to scroll across the screen when this item is added to
+ * the status bar.
+ */
+ public CharSequence tickerText;
+
+ /**
+ * The view that shows when this notification is shown in the expanded status bar.
+ */
+ public RemoteViews contentView;
+
+ /**
+ * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
+ * leave it at its default value of 0.
+ *
+ * @see android.widget.ImageView#setImageLevel
+ * @see android.graphics.drawable#setLevel
+ */
+ public int iconLevel;
+
+ /**
+ * The sound to play.
+ *
+ * <p>
+ * To play the default notification sound, see {@link #defaults}.
+ * </p>
+ */
+ public Uri sound;
+
+ /**
+ * Use this constant as the value for audioStreamType to request that
+ * the default stream type for notifications be used. Currently the
+ * default stream type is STREAM_RING.
+ */
+ public static final int STREAM_DEFAULT = -1;
+
+ /**
+ * The audio stream type to use when playing the sound.
+ * Should be one of the STREAM_ constants from
+ * {@link android.media.AudioManager}.
+ */
+ public int audioStreamType = STREAM_DEFAULT;
+
+
+ /**
+ * The pattern with which to vibrate. This pattern will repeat if {@link
+ * #FLAG_INSISTENT} bit is set in the {@link #flags} field.
+ *
+ * <p>
+ * To vibrate the default pattern, see {@link #defaults}.
+ * </p>
+ *
+ * @see android.os.Vibrator#vibrate(long[],int)
+ */
+ public long[] vibrate;
+
+ /**
+ * The color of the led. The hardware will do its best approximation.
+ *
+ * @see #FLAG_SHOW_LIGHTS
+ * @see #flags
+ */
+ public int ledARGB;
+
+ /**
+ * The number of milliseconds for the LED to be on while it's flashing.
+ * The hardware will do its best approximation.
+ *
+ * @see #FLAG_SHOW_LIGHTS
+ * @see #flags
+ */
+ public int ledOnMS;
+
+ /**
+ * The number of milliseconds for the LED to be off while it's flashing.
+ * The hardware will do its best approximation.
+ *
+ * @see #FLAG_SHOW_LIGHTS
+ * @see #flags
+ */
+ public int ledOffMS;
+
+ /**
+ * Specifies which values should be taken from the defaults.
+ * <p>
+ * To set, OR the desired from {@link #DEFAULT_SOUND},
+ * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
+ * values, use {@link #DEFAULT_ALL}.
+ * </p>
+ */
+ public int defaults;
+
+
+ /**
+ * Bit to be bitwise-ored into the {@link #flags} field that should be
+ * set if you want the LED on for this notification.
+ * <ul>
+ * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
+ * or 0 for both ledOnMS and ledOffMS.</li>
+ * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
+ * <li>To flash the LED, pass the number of milliseconds that it should
+ * be on and off to ledOnMS and ledOffMS.</li>
+ * </ul>
+ * <p>
+ * Since hardware varies, you are not guaranteed that any of the values
+ * you pass are honored exactly. Use the system defaults (TODO) if possible
+ * because they will be set to values that work on any given hardware.
+ * <p>
+ * The alpha channel must be set for forward compatibility.
+ *
+ */
+ public static final int FLAG_SHOW_LIGHTS = 0x00000001;
+
+ /**
+ * Bit to be bitwise-ored into the {@link #flags} field that should be
+ * set if this notification is in reference to something that is ongoing,
+ * like a phone call. It should not be set if this notification is in
+ * reference to something that happened at a particular point in time,
+ * like a missed phone call.
+ */
+ public static final int FLAG_ONGOING_EVENT = 0x00000002;
+
+ /**
+ * Bit to be bitwise-ored into the {@link #flags} field that if set,
+ * the audio and vibration will be repeated until the notification is
+ * cancelled.
+ *
+ * <p>
+ * NOTE: This notion will change when we have decided exactly
+ * what the UI will be.
+ * </p>
+ */
+ public static final int FLAG_INSISTENT = 0x00000004;
+
+ /**
+ * Bit to be bitwise-ored into the {@link #flags} field that should be
+ * set if you want the sound and/or vibration play each time the
+ * notification is sent, even if it has not been canceled before that.
+ */
+ public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
+
+ /**
+ * Bit to be bitwise-ored into the {@link #flags} field that should be
+ * set if the notification should be canceled when it is clicked by the
+ * user.
+ */
+ public static final int FLAG_AUTO_CANCEL = 0x00000010;
+
+ /**
+ * Bit to be bitwise-ored into the {@link #flags} field that should be
+ * set if the notification should not be canceled when the user clicks
+ * the Clear all button.
+ */
+ public static final int FLAG_NO_CLEAR = 0x00000020;
+
+ public int flags;
+
+ /**
+ * Constructs a Notification object with everything set to 0.
+ */
+ public Notification()
+ {
+ this.when = System.currentTimeMillis();
+ }
+
+ /**
+ * @deprecated use {@link #Notification(int,CharSequence,long)} and {@link #setLatestEventInfo}.
+ * @hide
+ */
+ public Notification(Context context, int icon, CharSequence tickerText, long when,
+ CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
+ {
+ this.when = when;
+ this.icon = icon;
+ this.tickerText = tickerText;
+ setLatestEventInfo(context, contentTitle, contentText,
+ PendingIntent.getActivity(context, 0, contentIntent, 0));
+ }
+
+ /**
+ * Constructs a Notification object with the information needed to
+ * have a status bar icon without the standard expanded view.
+ *
+ * @param icon The resource id of the icon to put in the status bar.
+ * @param tickerText The text that flows by in the status bar when the notification first
+ * activates.
+ * @param when The time to show in the time field. In the System.currentTimeMillis
+ * timebase.
+ */
+ public Notification(int icon, CharSequence tickerText, long when)
+ {
+ this.icon = icon;
+ this.tickerText = tickerText;
+ this.when = when;
+ }
+
+ /**
+ * Unflatten the notification from a parcel.
+ */
+ public Notification(Parcel parcel)
+ {
+ int version = parcel.readInt();
+
+ when = parcel.readLong();
+ icon = parcel.readInt();
+ number = parcel.readInt();
+ if (parcel.readInt() != 0) {
+ contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
+ }
+ if (parcel.readInt() != 0) {
+ deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
+ }
+ if (parcel.readInt() != 0) {
+ tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ }
+ if (parcel.readInt() != 0) {
+ contentView = RemoteViews.CREATOR.createFromParcel(parcel);
+ }
+ defaults = parcel.readInt();
+ flags = parcel.readInt();
+ if (parcel.readInt() != 0) {
+ sound = Uri.CREATOR.createFromParcel(parcel);
+ }
+
+ audioStreamType = parcel.readInt();
+ vibrate = parcel.createLongArray();
+ ledARGB = parcel.readInt();
+ ledOnMS = parcel.readInt();
+ ledOffMS = parcel.readInt();
+ iconLevel = parcel.readInt();
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Flatten this notification from a parcel.
+ */
+ public void writeToParcel(Parcel parcel, int flags)
+ {
+ parcel.writeInt(1);
+
+ parcel.writeLong(when);
+ parcel.writeInt(icon);
+ parcel.writeInt(number);
+ if (contentIntent != null) {
+ parcel.writeInt(1);
+ contentIntent.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (deleteIntent != null) {
+ parcel.writeInt(1);
+ deleteIntent.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (tickerText != null) {
+ parcel.writeInt(1);
+ TextUtils.writeToParcel(tickerText, parcel, flags);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (contentView != null) {
+ parcel.writeInt(1);
+ contentView.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeInt(defaults);
+ parcel.writeInt(this.flags);
+
+ if (sound != null) {
+ parcel.writeInt(1);
+ sound.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+ parcel.writeInt(audioStreamType);
+ parcel.writeLongArray(vibrate);
+ parcel.writeInt(ledARGB);
+ parcel.writeInt(ledOnMS);
+ parcel.writeInt(ledOffMS);
+ parcel.writeInt(iconLevel);
+ }
+
+ /**
+ * Parcelable.Creator that instantiates Notification objects
+ */
+ public static final Parcelable.Creator<Notification> CREATOR
+ = new Parcelable.Creator<Notification>()
+ {
+ public Notification createFromParcel(Parcel parcel)
+ {
+ return new Notification(parcel);
+ }
+
+ public Notification[] newArray(int size)
+ {
+ return new Notification[size];
+ }
+ };
+
+ /**
+ * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
+ * layout.
+ *
+ * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
+ * in the view.</p>
+ * @param context The context for your application / activity.
+ * @param contentTitle The title that goes in the expanded entry.
+ * @param contentText The text that goes in the expanded entry.
+ * @param contentIntent The intent to launch when the user clicks the expanded notification.
+ * If this is an activity, it must include the
+ * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
+ * that you take care of task management as described in
+ * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application Fundamentals: Activities and Tasks</a>.
+ */
+ public void setLatestEventInfo(Context context,
+ CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
+ RemoteViews contentView = new RemoteViews(context.getPackageName(),
+ com.android.internal.R.layout.status_bar_latest_event_content);
+ if (this.icon != 0) {
+ contentView.setImageViewResource(com.android.internal.R.id.icon, this.icon);
+ }
+ if (contentTitle != null) {
+ contentView.setTextViewText(com.android.internal.R.id.title, contentTitle);
+ }
+ if (contentText != null) {
+ contentView.setTextViewText(com.android.internal.R.id.text, contentText);
+ }
+ if (this.when != 0) {
+ Date date = new Date(when);
+ CharSequence str =
+ DateUtils.isToday(when) ? DateFormat.getTimeFormat(context).format(date)
+ : DateFormat.getDateFormat(context).format(date);
+ contentView.setTextViewText(com.android.internal.R.id.time, str);
+ }
+
+ this.contentView = contentView;
+ this.contentIntent = contentIntent;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Notification(vibrate=");
+ if (this.vibrate != null) {
+ int N = this.vibrate.length-1;
+ sb.append("[");
+ for (int i=0; i<N; i++) {
+ sb.append(this.vibrate[i]);
+ sb.append(',');
+ }
+ sb.append(this.vibrate[N]);
+ sb.append("]");
+ } else if ((this.defaults & DEFAULT_VIBRATE) != 0) {
+ sb.append("default");
+ } else {
+ sb.append("null");
+ }
+ sb.append(",sound=");
+ if (this.sound != null) {
+ sb.append(this.sound.toString());
+ } else if ((this.defaults & DEFAULT_SOUND) != 0) {
+ sb.append("default");
+ } else {
+ sb.append("null");
+ }
+ sb.append(",defaults=0x");
+ sb.append(Integer.toHexString(this.defaults));
+ sb.append(")");
+ return sb.toString();
+ }
+}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
new file mode 100644
index 0000000..39edab7
--- /dev/null
+++ b/core/java/android/app/NotificationManager.java
@@ -0,0 +1,134 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * Class to notify the user of events that happen. This is how you tell
+ * the user that something has happened in the background. {@more}
+ *
+ * Notifications can take different forms:
+ * <ul>
+ * <li>A persistent icon that goes in the status bar and is accessible
+ * through the launcher, (when the user selects it, a designated Intent
+ * can be launched),</li>
+ * <li>Turning on or flashing LEDs on the device, or</li>
+ * <li>Alerting the user by flashing the backlight, playing a sound,
+ * or vibrating.</li>
+ * </ul>
+ *
+ * <p>
+ * Each of the notify methods takes an int id parameter. This id identifies
+ * this notification from your app to the system, so that id should be unique
+ * within your app. If you call one of the notify methods with an id that is
+ * currently active and a new set of notification parameters, it will be
+ * updated. For example, if you pass a new status bar icon, the old icon in
+ * the status bar will be replaced with the new one. This is also the same
+ * id you pass to the {@link #cancel} method to clear this notification.
+ *
+ * <p>
+ * You do not instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService}.
+ *
+ * @see android.app.Notification
+ * @see android.content.Context#getSystemService
+ */
+public class NotificationManager
+{
+ private static String TAG = "NotificationManager";
+ private static boolean DEBUG = false;
+ private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+
+ private static INotificationManager sService;
+
+ static private INotificationManager getService()
+ {
+ if (sService != null) {
+ return sService;
+ }
+ IBinder b = ServiceManager.getService("notification");
+ sService = INotificationManager.Stub.asInterface(b);
+ return sService;
+ }
+
+ /*package*/ NotificationManager(Context context, Handler handler)
+ {
+ mContext = context;
+ }
+
+ /**
+ * Persistent notification on the status bar,
+ *
+ * @param id An identifier for this notification unique within your
+ * application.
+ * @param notification A {@link Notification} object describing how to
+ * notify the user, other than the view you're providing. Must not be null.
+ */
+ public void notify(int id, Notification notification)
+ {
+ int[] idOut = new int[1];
+ INotificationManager service = getService();
+ String pkg = mContext.getPackageName();
+ if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
+ try {
+ service.enqueueNotification(pkg, id, notification, idOut);
+ if (id != idOut[0]) {
+ Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Cancel a previously shown notification. If it's transient, the view
+ * will be hidden. If it's persistent, it will be removed from the status
+ * bar.
+ */
+ public void cancel(int id)
+ {
+ INotificationManager service = getService();
+ String pkg = mContext.getPackageName();
+ if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
+ try {
+ service.cancelNotification(pkg, id);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Cancel all previously shown notifications. See {@link #cancel} for the
+ * detailed behavior.
+ */
+ public void cancelAll()
+ {
+ INotificationManager service = getService();
+ String pkg = mContext.getPackageName();
+ if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
+ try {
+ service.cancelAllNotifications(pkg);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private Context mContext;
+}
diff --git a/core/java/android/app/PendingIntent.aidl b/core/java/android/app/PendingIntent.aidl
new file mode 100644
index 0000000..f0d530c
--- /dev/null
+++ b/core/java/android/app/PendingIntent.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.app;
+
+parcelable PendingIntent;
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
new file mode 100644
index 0000000..1bed706
--- /dev/null
+++ b/core/java/android/app/PendingIntent.java
@@ -0,0 +1,508 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AndroidException;
+
+/**
+ * A description of an Intent and target action to perform with it. Instances
+ * of this class are created with {@link #getActivity},
+ * {@link #getBroadcast}, {@link #getService}; the returned object can be
+ * handed to other applications so that they can perform the action you
+ * described on your behalf at a later time.
+ *
+ * <p>By giving a PendingIntent to another application,
+ * you are granting it the right to perform the operation you have specified
+ * as if the other application was yourself (with the same permissions and
+ * identity). As such, you should be careful about how you build the PendingIntent:
+ * often, for example, the base Intent you supply will have the component
+ * name explicitly set to one of your own components, to ensure it is ultimately
+ * sent there and nowhere else.
+ *
+ * <p>A PendingIntent itself is simply a reference to a token maintained by
+ * the system describing the original data used to retrieve it. This means
+ * that, even if its owning application's process is killed, the
+ * PendingIntent itself will remain usable from other processes that
+ * have been given it. If the creating application later re-retrieves the
+ * same kind of PendingIntent (same operation, same Intent action, data,
+ * categories, and components, and same flags), it will receive a PendingIntent
+ * representing the same token if that is still valid, and can thus call
+ * {@link #cancel} to remove it.
+ */
+public final class PendingIntent implements Parcelable {
+ private final IIntentSender mTarget;
+
+ /**
+ * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and
+ * {@link #getService}: this
+ * PendingIntent can only be used once. If set, after
+ * {@link #send()} is called on it, it will be automatically
+ * canceled for you and any future attempt to send through it will fail.
+ */
+ public static final int FLAG_ONE_SHOT = 1<<30;
+ /**
+ * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and
+ * {@link #getService}: if the described PendingIntent does not already
+ * exist, then simply return null instead of creating it.
+ */
+ public static final int FLAG_NO_CREATE = 1<<29;
+ /**
+ * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and
+ * {@link #getService}: if the described PendingIntent already exists,
+ * the current one is canceled before generating a new one. You can use
+ * this to retrieve a new PendingIntent when you are only changing the
+ * extra data in the Intent; by canceling the previous pending intent,
+ * this ensures that only entities given the new data will be able to
+ * launch it. If this assurance is not an issue, consider
+ * {@link #FLAG_UPDATE_CURRENT}.
+ */
+ public static final int FLAG_CANCEL_CURRENT = 1<<28;
+ /**
+ * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and
+ * {@link #getService}: if the described PendingIntent already exists,
+ * then keep it but its replace its extra data with what is in this new
+ * Intent. This can be used if you are creating intents where only the
+ * extras change, and don't care that any entities that received your
+ * previous PendingIntent will be able to launch it with your new
+ * extras even if they are not explicitly given to it.
+ */
+ public static final int FLAG_UPDATE_CURRENT = 1<<27;
+
+ /**
+ * Exception thrown when trying to send through a PendingIntent that
+ * has been canceled or is otherwise no longer able to execute the request.
+ */
+ public static class CanceledException extends AndroidException {
+ public CanceledException() {
+ }
+
+ public CanceledException(String name) {
+ super(name);
+ }
+
+ public CanceledException(Exception cause) {
+ super(cause);
+ }
+ };
+
+ /**
+ * Callback interface for discovering when a send operation has
+ * completed. Primarily for use with a PendingIntent that is
+ * performing a broadcast, this provides the same information as
+ * calling {@link Context#sendOrderedBroadcast(Intent, String,
+ * android.content.BroadcastReceiver, Handler, int, String, Bundle)
+ * Context.sendBroadcast()} with a final BroadcastReceiver.
+ */
+ public interface OnFinished {
+ /**
+ * Called when a send operation as completed.
+ *
+ * @param pendingIntent The PendingIntent this operation was sent through.
+ * @param intent The original Intent that was sent.
+ * @param resultCode The final result code determined by the send.
+ * @param resultData The final data collected by a broadcast.
+ * @param resultExtras The final extras collected by a broadcast.
+ */
+ void onSendFinished(PendingIntent pendingIntent, Intent intent,
+ int resultCode, String resultData, Bundle resultExtras);
+ }
+
+ private static class FinishedDispatcher extends IIntentReceiver.Stub
+ implements Runnable {
+ private final PendingIntent mPendingIntent;
+ private final OnFinished mWho;
+ private final Handler mHandler;
+ private Intent mIntent;
+ private int mResultCode;
+ private String mResultData;
+ private Bundle mResultExtras;
+ FinishedDispatcher(PendingIntent pi, OnFinished who, Handler handler) {
+ mPendingIntent = pi;
+ mWho = who;
+ mHandler = handler;
+ }
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean serialized) {
+ mIntent = intent;
+ mResultCode = resultCode;
+ mResultData = data;
+ mResultExtras = extras;
+ if (mHandler == null) {
+ run();
+ } else {
+ mHandler.post(this);
+ }
+ }
+ public void run() {
+ mWho.onSendFinished(mPendingIntent, mIntent, mResultCode,
+ mResultData, mResultExtras);
+ }
+ }
+
+ /**
+ * Retrieve a PendingIntent that will start a new activity, like calling
+ * {@link Context#startActivity(Intent) Context.startActivity(Intent)}.
+ * Note that the activity will be started outside of the context of an
+ * existing activity, so you must use the {@link Intent#FLAG_ACTIVITY_NEW_TASK
+ * Intent.FLAG_ACTIVITY_NEW_TASK} launch flag in the Intent.
+ *
+ * @param context The Context in which this PendingIntent should start
+ * the activity.
+ * @param requestCode Private request code for the sender (currently
+ * not used).
+ * @param intent Intent of the activity to be launched.
+ * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
+ * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
+ * or any of the flags as supported by
+ * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
+ * of the intent that can be supplied when the actual send happens.
+ *
+ * @return Returns an existing or new PendingIntent matching the given
+ * parameters. May return null only if {@link #FLAG_NO_CREATE} has been
+ * supplied.
+ */
+ public static PendingIntent getActivity(Context context, int requestCode,
+ Intent intent, int flags) {
+ String packageName = context.getPackageName();
+ String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
+ context.getContentResolver()) : null;
+ try {
+ IIntentSender target =
+ ActivityManagerNative.getDefault().getIntentSender(
+ IActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ null, null, requestCode, intent, resolvedType, flags);
+ return target != null ? new PendingIntent(target) : null;
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve a PendingIntent that will perform a broadcast, like calling
+ * {@link Context#sendBroadcast(Intent) Context.sendBroadcast()}.
+ *
+ * @param context The Context in which this PendingIntent should perform
+ * the broadcast.
+ * @param requestCode Private request code for the sender (currently
+ * not used).
+ * @param intent The Intent to be broadcast.
+ * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
+ * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
+ * or any of the flags as supported by
+ * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
+ * of the intent that can be supplied when the actual send happens.
+ *
+ * @return Returns an existing or new PendingIntent matching the given
+ * parameters. May return null only if {@link #FLAG_NO_CREATE} has been
+ * supplied.
+ */
+ public static PendingIntent getBroadcast(Context context, int requestCode,
+ Intent intent, int flags) {
+ String packageName = context.getPackageName();
+ String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
+ context.getContentResolver()) : null;
+ try {
+ IIntentSender target =
+ ActivityManagerNative.getDefault().getIntentSender(
+ IActivityManager.INTENT_SENDER_BROADCAST, packageName,
+ null, null, requestCode, intent, resolvedType, flags);
+ return target != null ? new PendingIntent(target) : null;
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve a PendingIntent that will start a service, like calling
+ * {@link Context#startService Context.startService()}. The start
+ * arguments given to the service will come from the extras of the Intent.
+ *
+ * @param context The Context in which this PendingIntent should start
+ * the service.
+ * @param requestCode Private request code for the sender (currently
+ * not used).
+ * @param intent An Intent describing the service to be started.
+ * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
+ * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
+ * or any of the flags as supported by
+ * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
+ * of the intent that can be supplied when the actual send happens.
+ *
+ * @return Returns an existing or new PendingIntent matching the given
+ * parameters. May return null only if {@link #FLAG_NO_CREATE} has been
+ * supplied.
+ */
+ public static PendingIntent getService(Context context, int requestCode,
+ Intent intent, int flags) {
+ String packageName = context.getPackageName();
+ String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
+ context.getContentResolver()) : null;
+ try {
+ IIntentSender target =
+ ActivityManagerNative.getDefault().getIntentSender(
+ IActivityManager.INTENT_SENDER_SERVICE, packageName,
+ null, null, requestCode, intent, resolvedType, flags);
+ return target != null ? new PendingIntent(target) : null;
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /**
+ * Cancel a currently active PendingIntent. Only the original application
+ * owning an PendingIntent can cancel it.
+ */
+ public void cancel() {
+ try {
+ ActivityManagerNative.getDefault().cancelIntentSender(mTarget);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Perform the operation associated with this PendingIntent.
+ *
+ * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler)
+ *
+ * @throws CanceledException Throws CanceledException if the PendingIntent
+ * is no longer allowing more intents to be sent through it.
+ */
+ public void send() throws CanceledException {
+ send(null, 0, null, null, null);
+ }
+
+ /**
+ * Perform the operation associated with this PendingIntent.
+ *
+ * @param code Result code to supply back to the PendingIntent's target.
+ *
+ * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler)
+ *
+ * @throws CanceledException Throws CanceledException if the PendingIntent
+ * is no longer allowing more intents to be sent through it.
+ */
+ public void send(int code) throws CanceledException {
+ send(null, code, null, null, null);
+ }
+
+ /**
+ * Perform the operation associated with this PendingIntent, allowing the
+ * caller to specify information about the Intent to use.
+ *
+ * @param context The Context of the caller.
+ * @param code Result code to supply back to the PendingIntent's target.
+ * @param intent Additional Intent data. See {@link Intent#fillIn
+ * Intent.fillIn()} for information on how this is applied to the
+ * original Intent.
+ *
+ * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler)
+ *
+ * @throws CanceledException Throws CanceledException if the PendingIntent
+ * is no longer allowing more intents to be sent through it.
+ */
+ public void send(Context context, int code, Intent intent)
+ throws CanceledException {
+ send(context, code, intent, null, null);
+ }
+
+ /**
+ * Perform the operation associated with this PendingIntent, allowing the
+ * caller to be notified when the send has completed.
+ *
+ * @param code Result code to supply back to the PendingIntent's target.
+ * @param onFinished The object to call back on when the send has
+ * completed, or null for no callback.
+ * @param handler Handler identifying the thread on which the callback
+ * should happen. If null, the callback will happen from the thread
+ * pool of the process.
+ *
+ * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler)
+ *
+ * @throws CanceledException Throws CanceledException if the PendingIntent
+ * is no longer allowing more intents to be sent through it.
+ */
+ public void send(int code, OnFinished onFinished, Handler handler)
+ throws CanceledException {
+ send(null, code, null, onFinished, handler);
+ }
+
+ /**
+ * Perform the operation associated with this PendingIntent, allowing the
+ * caller to specify information about the Intent to use and be notified
+ * when the send has completed.
+ *
+ * <p>For the intent parameter, a PendingIntent
+ * often has restrictions on which fields can be supplied here, based on
+ * how the PendingIntent was retrieved in {@link #getActivity},
+ * {@link #getBroadcast}, or {@link #getService}.
+ *
+ * @param context The Context of the caller. This may be null if
+ * <var>intent</var> is also null.
+ * @param code Result code to supply back to the PendingIntent's target.
+ * @param intent Additional Intent data. See {@link Intent#fillIn
+ * Intent.fillIn()} for information on how this is applied to the
+ * original Intent. Use null to not modify the original Intent.
+ * @param onFinished The object to call back on when the send has
+ * completed, or null for no callback.
+ * @param handler Handler identifying the thread on which the callback
+ * should happen. If null, the callback will happen from the thread
+ * pool of the process.
+ *
+ * @see #send()
+ * @see #send(int)
+ * @see #send(Context, int, Intent)
+ * @see #send(int, android.app.PendingIntent.OnFinished, Handler)
+ *
+ * @throws CanceledException Throws CanceledException if the PendingIntent
+ * is no longer allowing more intents to be sent through it.
+ */
+ public void send(Context context, int code, Intent intent,
+ OnFinished onFinished, Handler handler) throws CanceledException {
+ try {
+ String resolvedType = intent != null ?
+ intent.resolveTypeIfNeeded(context.getContentResolver())
+ : null;
+ int res = mTarget.send(code, intent, resolvedType,
+ onFinished != null
+ ? new FinishedDispatcher(this, onFinished, handler)
+ : null);
+ if (res < 0) {
+ throw new CanceledException();
+ }
+ } catch (RemoteException e) {
+ throw new CanceledException(e);
+ }
+ }
+
+ /**
+ * Return the package name of the application that created this
+ * PendingIntent, that is the identity under which you will actually be
+ * sending the Intent. The returned string is supplied by the system, so
+ * that an application can not spoof its package.
+ *
+ * @return The package name of the PendingIntent, or null if there is
+ * none associated with it.
+ */
+ public String getTargetPackage() {
+ try {
+ return ActivityManagerNative.getDefault()
+ .getPackageForIntentSender(mTarget);
+ } catch (RemoteException e) {
+ // Should never happen.
+ return null;
+ }
+ }
+
+ /**
+ * Comparison operator on two PendingIntent objects, such that true
+ * is returned then they both represent the same operation from the
+ * same package. This allows you to use {@link #getActivity},
+ * {@link #getBroadcast}, or {@link #getService} multiple times (even
+ * across a process being killed), resulting in different PendingIntent
+ * objects but whose equals() method identifies them as being the same
+ * operation.
+ */
+ @Override
+ public boolean equals(Object otherObj) {
+ if (otherObj instanceof PendingIntent) {
+ return mTarget.asBinder().equals(((PendingIntent)otherObj)
+ .mTarget.asBinder());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mTarget.asBinder().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "PendingIntent{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " target " + (mTarget != null ? mTarget.asBinder() : null) + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeStrongBinder(mTarget.asBinder());
+ }
+
+ public static final Parcelable.Creator<PendingIntent> CREATOR
+ = new Parcelable.Creator<PendingIntent>() {
+ public PendingIntent createFromParcel(Parcel in) {
+ IBinder target = in.readStrongBinder();
+ return target != null ? new PendingIntent(target) : null;
+ }
+
+ public PendingIntent[] newArray(int size) {
+ return new PendingIntent[size];
+ }
+ };
+
+ /**
+ * Convenience function for writing either a PendingIntent or null pointer to
+ * a Parcel. You must use this with {@link #readPendingIntentOrNullFromParcel}
+ * for later reading it.
+ *
+ * @param sender The PendingIntent to write, or null.
+ * @param out Where to write the PendingIntent.
+ */
+ public static void writePendingIntentOrNullToParcel(PendingIntent sender,
+ Parcel out) {
+ out.writeStrongBinder(sender != null ? sender.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 #writePendingIntentOrNullToParcel}.
+ *
+ * @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 PendingIntent readPendingIntentOrNullFromParcel(Parcel in) {
+ IBinder b = in.readStrongBinder();
+ return b != null ? new PendingIntent(b) : null;
+ }
+
+ /*package*/ PendingIntent(IIntentSender target) {
+ mTarget = target;
+ }
+
+ /*package*/ PendingIntent(IBinder target) {
+ mTarget = IIntentSender.Stub.asInterface(target);
+ }
+
+ /*package*/ IIntentSender getTarget() {
+ return mTarget;
+ }
+}
diff --git a/core/java/android/app/ProgressDialog.java b/core/java/android/app/ProgressDialog.java
new file mode 100644
index 0000000..c87e398
--- /dev/null
+++ b/core/java/android/app/ProgressDialog.java
@@ -0,0 +1,301 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+import java.text.NumberFormat;
+
+/**
+ * <p>A dialog showing a progress indicator and an optional text message or view.
+ * Only a text message or a view can be used at the same time.</p>
+ * <p>The dialog can be made cancelable on back key press.</p>
+ * <p>The progress range is 0..10000.</p>
+ */
+public class ProgressDialog extends AlertDialog {
+
+ /** Creates a ProgressDialog with a ciruclar, spinning progress
+ * bar. This is the default.
+ */
+ public static final int STYLE_SPINNER = 0;
+
+ /** Creates a ProgressDialog with a horizontal progress bar.
+ */
+ public static final int STYLE_HORIZONTAL = 1;
+
+ private ProgressBar mProgress;
+ private TextView mMessageView;
+
+ private int mProgressStyle = STYLE_SPINNER;
+ private TextView mProgressNumber;
+ private TextView mProgressPercent;
+ private NumberFormat mProgressPercentFormat;
+
+ private int mMax;
+ private int mProgressVal;
+ private int mSecondaryProgressVal;
+ private int mIncrementBy;
+ private int mIncrementSecondaryBy;
+ private Drawable mProgressDrawable;
+ private Drawable mIndeterminateDrawable;
+ private CharSequence mMessage;
+ private boolean mIndeterminate;
+
+ private boolean mHasStarted;
+ private Handler mViewUpdateHandler;
+
+ public ProgressDialog(Context context) {
+ this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+ }
+
+ public ProgressDialog(Context context, int theme) {
+ super(context, theme);
+ }
+
+ public static ProgressDialog show(Context context, CharSequence title,
+ CharSequence message) {
+ return show(context, title, message, false);
+ }
+
+ public static ProgressDialog show(Context context, CharSequence title,
+ CharSequence message, boolean indeterminate) {
+ return show(context, title, message, indeterminate, false, null);
+ }
+
+ public static ProgressDialog show(Context context, CharSequence title,
+ CharSequence message, boolean indeterminate, boolean cancelable) {
+ return show(context, title, message, indeterminate, cancelable, null);
+ }
+
+ public static ProgressDialog show(Context context, CharSequence title,
+ CharSequence message, boolean indeterminate,
+ boolean cancelable, OnCancelListener cancelListener) {
+ ProgressDialog dialog = new ProgressDialog(context);
+ dialog.setTitle(title);
+ dialog.setMessage(message);
+ dialog.setIndeterminate(indeterminate);
+ dialog.setCancelable(cancelable);
+ dialog.setOnCancelListener(cancelListener);
+ dialog.show();
+ return dialog;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ if (mProgressStyle == STYLE_HORIZONTAL) {
+
+ /* Use a separate handler to update the text views as they
+ * must be updated on the same thread that created them.
+ */
+ mViewUpdateHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+
+ /* Update the number and percent */
+ int progress = mProgress.getProgress();
+ int max = mProgress.getMax();
+ double percent = (double) progress / (double) max;
+ mProgressNumber.setText(progress + "/" + max);
+ mProgressPercent.setText(mProgressPercentFormat.format(percent));
+ }
+ };
+ View view = inflater.inflate(R.layout.alert_dialog_progress, null);
+ mProgress = (ProgressBar) view.findViewById(R.id.progress);
+ mProgressNumber = (TextView) view.findViewById(R.id.progress_number);
+ mProgressPercent = (TextView) view.findViewById(R.id.progress_percent);
+ mProgressPercentFormat = NumberFormat.getPercentInstance();
+ mProgressPercentFormat.setMaximumFractionDigits(0);
+ setView(view);
+ } else {
+ View view = inflater.inflate(R.layout.progress_dialog, null);
+ mProgress = (ProgressBar) view.findViewById(R.id.progress);
+ mMessageView = (TextView) view.findViewById(R.id.message);
+ setView(view);
+ }
+ if (mMax > 0) {
+ setMax(mMax);
+ }
+ if (mProgressVal > 0) {
+ setProgress(mProgressVal);
+ }
+ if (mSecondaryProgressVal > 0) {
+ setSecondaryProgress(mSecondaryProgressVal);
+ }
+ if (mIncrementBy > 0) {
+ incrementProgressBy(mIncrementBy);
+ }
+ if (mIncrementSecondaryBy > 0) {
+ incrementSecondaryProgressBy(mIncrementSecondaryBy);
+ }
+ if (mProgressDrawable != null) {
+ setProgressDrawable(mProgressDrawable);
+ }
+ if (mIndeterminateDrawable != null) {
+ setIndeterminateDrawable(mIndeterminateDrawable);
+ }
+ if (mMessage != null) {
+ setMessage(mMessage);
+ }
+ setIndeterminate(mIndeterminate);
+ onProgressChanged();
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mHasStarted = true;
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mHasStarted = false;
+ }
+
+ public void setProgress(int value) {
+ if (mHasStarted) {
+ mProgress.setProgress(value);
+ onProgressChanged();
+ } else {
+ mProgressVal = value;
+ }
+ }
+
+ public void setSecondaryProgress(int secondaryProgress) {
+ if (mProgress != null) {
+ mProgress.setSecondaryProgress(secondaryProgress);
+ onProgressChanged();
+ } else {
+ mSecondaryProgressVal = secondaryProgress;
+ }
+ }
+
+ public int getProgress() {
+ if (mProgress != null) {
+ return mProgress.getProgress();
+ }
+ return mProgressVal;
+ }
+
+ public int getSecondaryProgress() {
+ if (mProgress != null) {
+ return mProgress.getSecondaryProgress();
+ }
+ return mSecondaryProgressVal;
+ }
+
+ public int getMax() {
+ if (mProgress != null) {
+ return mProgress.getMax();
+ }
+ return mMax;
+ }
+
+ public void setMax(int max) {
+ if (mProgress != null) {
+ mProgress.setMax(max);
+ onProgressChanged();
+ } else {
+ mMax = max;
+ }
+ }
+
+ public void incrementProgressBy(int diff) {
+ if (mProgress != null) {
+ mProgress.incrementProgressBy(diff);
+ onProgressChanged();
+ } else {
+ mIncrementBy += diff;
+ }
+ }
+
+ public void incrementSecondaryProgressBy(int diff) {
+ if (mProgress != null) {
+ mProgress.incrementSecondaryProgressBy(diff);
+ onProgressChanged();
+ } else {
+ mIncrementSecondaryBy += diff;
+ }
+ }
+
+ public void setProgressDrawable(Drawable d) {
+ if (mProgress != null) {
+ mProgress.setProgressDrawable(d);
+ } else {
+ mProgressDrawable = d;
+ }
+ }
+
+ public void setIndeterminateDrawable(Drawable d) {
+ if (mProgress != null) {
+ mProgress.setIndeterminateDrawable(d);
+ } else {
+ mIndeterminateDrawable = d;
+ }
+ }
+
+ public void setIndeterminate(boolean indeterminate) {
+ if (mProgress != null) {
+ mProgress.setIndeterminate(indeterminate);
+ } else {
+ mIndeterminate = indeterminate;
+ }
+ }
+
+ public boolean isIndeterminate() {
+ if (mProgress != null) {
+ return mProgress.isIndeterminate();
+ }
+ return mIndeterminate;
+ }
+
+ @Override
+ public void setMessage(CharSequence message) {
+ if (mProgress != null) {
+ if (mProgressStyle == STYLE_HORIZONTAL) {
+ super.setMessage(message);
+ } else {
+ mMessageView.setText(message);
+ }
+ } else {
+ mMessage = message;
+ }
+ }
+
+ public void setProgressStyle(int style) {
+ mProgressStyle = style;
+ }
+
+ private void onProgressChanged() {
+ if (mProgressStyle == STYLE_HORIZONTAL) {
+ mViewUpdateHandler.sendEmptyMessage(0);
+ }
+ }
+}
diff --git a/core/java/android/app/ResultInfo.java b/core/java/android/app/ResultInfo.java
new file mode 100644
index 0000000..48a0fc2
--- /dev/null
+++ b/core/java/android/app/ResultInfo.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.app;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Bundle;
+
+import java.util.Map;
+
+/**
+ * {@hide}
+ */
+public class ResultInfo implements Parcelable {
+ public final String mResultWho;
+ public final int mRequestCode;
+ public final int mResultCode;
+ public final Intent mData;
+
+ public ResultInfo(String resultWho, int requestCode, int resultCode,
+ Intent data) {
+ mResultWho = resultWho;
+ mRequestCode = requestCode;
+ mResultCode = resultCode;
+ mData = data;
+ }
+
+ public String toString() {
+ return "ResultInfo{who=" + mResultWho + ", request=" + mRequestCode
+ + ", result=" + mResultCode + ", data=" + mData + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mResultWho);
+ out.writeInt(mRequestCode);
+ out.writeInt(mResultCode);
+ if (mData != null) {
+ out.writeInt(1);
+ mData.writeToParcel(out, 0);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ public static final Parcelable.Creator<ResultInfo> CREATOR
+ = new Parcelable.Creator<ResultInfo>() {
+ public ResultInfo createFromParcel(Parcel in) {
+ return new ResultInfo(in);
+ }
+
+ public ResultInfo[] newArray(int size) {
+ return new ResultInfo[size];
+ }
+ };
+
+ public ResultInfo(Parcel in) {
+ mResultWho = in.readString();
+ mRequestCode = in.readInt();
+ mResultCode = in.readInt();
+ if (in.readInt() != 0) {
+ mData = Intent.CREATOR.createFromParcel(in);
+ } else {
+ mData = null;
+ }
+ }
+}
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
new file mode 100644
index 0000000..d447eb2
--- /dev/null
+++ b/core/java/android/app/SearchDialog.java
@@ -0,0 +1,1593 @@
+/*
+ * 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.app;
+
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.server.search.SearchableInfo;
+import android.speech.RecognizerIntent;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.AutoCompleteTextView;
+import android.widget.Button;
+import android.widget.CursorAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+import android.widget.WrapperListAdapter;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * In-application-process implementation of Search Bar. This is still controlled by the
+ * SearchManager, but it runs in the current activity's process to keep things lighter weight.
+ *
+ * @hide
+ */
+public class SearchDialog extends Dialog implements OnItemClickListener, OnItemSelectedListener {
+
+ // Debugging support
+ final static String LOG_TAG = "SearchDialog";
+ private static final int DBG_LOG_TIMING = 0;
+ final static int DBG_JAM_THREADING = 0;
+
+ // interaction with runtime
+ IntentFilter mCloseDialogsFilter;
+ IntentFilter mPackageFilter;
+
+ private static final String INSTANCE_KEY_COMPONENT = "comp";
+ private static final String INSTANCE_KEY_APPDATA = "data";
+ private static final String INSTANCE_KEY_GLOBALSEARCH = "glob";
+ private static final String INSTANCE_KEY_DISPLAY_QUERY = "dQry";
+ private static final String INSTANCE_KEY_DISPLAY_SEL_START = "sel1";
+ private static final String INSTANCE_KEY_DISPLAY_SEL_END = "sel2";
+ private static final String INSTANCE_KEY_USER_QUERY = "uQry";
+ private static final String INSTANCE_KEY_SUGGESTION_QUERY = "sQry";
+ private static final String INSTANCE_KEY_SELECTED_ELEMENT = "slEl";
+ private static final int INSTANCE_SELECTED_BUTTON = -2;
+ private static final int INSTANCE_SELECTED_QUERY = -1;
+
+ // views & widgets
+ private TextView mBadgeLabel;
+ private AutoCompleteTextView mSearchTextField;
+ private Button mGoButton;
+ private ImageButton mVoiceButton;
+
+ // interaction with searchable application
+ private ComponentName mLaunchComponent;
+ private Bundle mAppSearchData;
+ private boolean mGlobalSearchMode;
+ private Context mActivityContext;
+
+ // interaction with the search manager service
+ private SearchableInfo mSearchable;
+
+ // support for suggestions
+ private String mUserQuery = null;
+ private int mUserQuerySelStart;
+ private int mUserQuerySelEnd;
+ private boolean mLeaveJammedQueryOnRefocus = false;
+ private String mPreviousSuggestionQuery = null;
+ private int mPresetSelection = -1;
+ private String mSuggestionAction = null;
+ private Uri mSuggestionData = null;
+ private String mSuggestionQuery = null;
+
+ // For voice searching
+ private Intent mVoiceWebSearchIntent;
+ private Intent mVoiceAppSearchIntent;
+
+ // support for AutoCompleteTextView suggestions display
+ private SuggestionsAdapter mSuggestionsAdapter;
+
+
+ /**
+ * Constructor - fires it up and makes it look like the search UI.
+ *
+ * @param context Application Context we can use for system acess
+ */
+ public SearchDialog(Context context) {
+ super(context, com.android.internal.R.style.Theme_SearchBar);
+ }
+
+ /**
+ * We create the search dialog just once, and it stays around (hidden)
+ * until activated by the user.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Window theWindow = getWindow();
+ theWindow.setGravity(Gravity.TOP|Gravity.FILL_HORIZONTAL);
+
+ setContentView(com.android.internal.R.layout.search_bar);
+
+ theWindow.setLayout(ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ WindowManager.LayoutParams lp = theWindow.getAttributes();
+ lp.setTitle("Search Dialog");
+ lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE;
+ theWindow.setAttributes(lp);
+
+ // get the view elements for local access
+ mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge);
+ mSearchTextField = (AutoCompleteTextView)
+ findViewById(com.android.internal.R.id.search_src_text);
+ mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn);
+ mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn);
+
+ // attach listeners
+ mSearchTextField.addTextChangedListener(mTextWatcher);
+ mSearchTextField.setOnKeyListener(mTextKeyListener);
+ mGoButton.setOnClickListener(mGoButtonClickListener);
+ mGoButton.setOnKeyListener(mButtonsKeyListener);
+ mVoiceButton.setOnClickListener(mVoiceButtonClickListener);
+ mVoiceButton.setOnKeyListener(mButtonsKeyListener);
+
+ // pre-hide all the extraneous elements
+ mBadgeLabel.setVisibility(View.GONE);
+
+ // Additional adjustments to make Dialog work for Search
+
+ // Touching outside of the search dialog will dismiss it
+ setCanceledOnTouchOutside(true);
+
+ // Set up broadcast filters
+ mCloseDialogsFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ mPackageFilter = new IntentFilter();
+ mPackageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ mPackageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ mPackageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ mPackageFilter.addDataScheme("package");
+
+ // Save voice intent for later queries/launching
+ mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
+ mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
+
+ mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+ }
+
+ /**
+ * Set up the search dialog
+ *
+ * @param Returns true if search dialog launched, false if not
+ */
+ public boolean show(String initialQuery, boolean selectInitialQuery,
+ ComponentName componentName, Bundle appSearchData, boolean globalSearch) {
+ if (isShowing()) {
+ // race condition - already showing but not handling events yet.
+ // in this case, just discard the "show" request
+ return true;
+ }
+
+ // Get searchable info from search manager and use to set up other elements of UI
+ // Do this first so we can get out quickly if there's nothing to search
+ ISearchManager sms;
+ sms = ISearchManager.Stub.asInterface(ServiceManager.getService(Context.SEARCH_SERVICE));
+ try {
+ mSearchable = sms.getSearchableInfo(componentName, globalSearch);
+ } catch (RemoteException e) {
+ mSearchable = null;
+ }
+ if (mSearchable == null) {
+ // unfortunately, we can't log here. it would be logspam every time the user
+ // clicks the "search" key on a non-search app
+ return false;
+ }
+
+ // OK, we're going to show ourselves
+ super.show();
+
+ setupSearchableInfo();
+
+ mLaunchComponent = componentName;
+ mAppSearchData = appSearchData;
+ mGlobalSearchMode = globalSearch;
+
+ // receive broadcasts
+ getContext().registerReceiver(mBroadcastReceiver, mCloseDialogsFilter);
+ getContext().registerReceiver(mBroadcastReceiver, mPackageFilter);
+
+ // configure the autocomplete aspects of the input box
+ mSearchTextField.setOnItemClickListener(this);
+ mSearchTextField.setOnItemSelectedListener(this);
+
+ // This conversion is necessary to force a preload of the EditText and thus force
+ // suggestions to be presented (even for an empty query)
+ if (initialQuery == null) {
+ initialQuery = ""; // This forces the preload to happen, triggering suggestions
+ }
+
+ // attach the suggestions adapter, if suggestions are available
+ // The existence of a suggestions authority is the proxy for "suggestions available here"
+ if (mSearchable.getSuggestAuthority() == null) {
+ mSuggestionsAdapter = null;
+ mSearchTextField.setAdapter(mSuggestionsAdapter);
+ mSearchTextField.setText(initialQuery);
+ } else {
+ mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable);
+ mSearchTextField.setAdapter(mSuggestionsAdapter);
+
+ // finally, load the user's initial text (which may trigger suggestions)
+ mSuggestionsAdapter.setNonUserQuery(false);
+ mSearchTextField.setText(initialQuery);
+ }
+
+ if (selectInitialQuery) {
+ mSearchTextField.selectAll();
+ } else {
+ mSearchTextField.setSelection(initialQuery.length());
+ }
+ return true;
+ }
+
+ /**
+ * The default show() for this Dialog is not supported.
+ */
+ @Override
+ public void show() {
+ return;
+ }
+
+ /**
+ * The search dialog is being dismissed, so handle all of the local shutdown operations.
+ *
+ * This function is designed to be idempotent so that dismiss() can be safely called at any time
+ * (even if already closed) and more likely to really dump any memory. No leaks!
+ */
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ setOnCancelListener(null);
+ setOnDismissListener(null);
+
+ // stop receiving broadcasts (throws exception if none registered)
+ try {
+ getContext().unregisterReceiver(mBroadcastReceiver);
+ } catch (RuntimeException e) {
+ // This is OK - it just means we didn't have any registered
+ }
+
+ // close any leftover cursor
+ if (mSuggestionsAdapter != null) {
+ mSuggestionsAdapter.changeCursor(null);
+ }
+
+ // dump extra memory we're hanging on to
+ mLaunchComponent = null;
+ mAppSearchData = null;
+ mSearchable = null;
+ mSuggestionAction = null;
+ mSuggestionData = null;
+ mSuggestionQuery = null;
+ mActivityContext = null;
+ mPreviousSuggestionQuery = null;
+ mUserQuery = null;
+ }
+
+ /**
+ * Save the minimal set of data necessary to recreate the search
+ *
+ * @return A bundle with the state of the dialog.
+ */
+ @Override
+ public Bundle onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+
+ // setup info so I can recreate this particular search
+ bundle.putParcelable(INSTANCE_KEY_COMPONENT, mLaunchComponent);
+ bundle.putBundle(INSTANCE_KEY_APPDATA, mAppSearchData);
+ bundle.putBoolean(INSTANCE_KEY_GLOBALSEARCH, mGlobalSearchMode);
+
+ // UI state
+ bundle.putString(INSTANCE_KEY_DISPLAY_QUERY, mSearchTextField.getText().toString());
+ bundle.putInt(INSTANCE_KEY_DISPLAY_SEL_START, mSearchTextField.getSelectionStart());
+ bundle.putInt(INSTANCE_KEY_DISPLAY_SEL_END, mSearchTextField.getSelectionEnd());
+ bundle.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
+ bundle.putString(INSTANCE_KEY_SUGGESTION_QUERY, mPreviousSuggestionQuery);
+
+ int selectedElement = INSTANCE_SELECTED_QUERY;
+ if (mGoButton.isFocused()) {
+ selectedElement = INSTANCE_SELECTED_BUTTON;
+ } else if (mSearchTextField.isPopupShowing()) {
+ selectedElement = 0; // TODO mSearchTextField.getListSelection() // 0..n
+ }
+ bundle.putInt(INSTANCE_KEY_SELECTED_ELEMENT, selectedElement);
+
+ return bundle;
+ }
+
+ /**
+ * Restore the state of the dialog from a previously saved bundle.
+ *
+ * @param savedInstanceState The state of the dialog previously saved by
+ * {@link #onSaveInstanceState()}.
+ */
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ // Get the launch info
+ ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT);
+ Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA);
+ boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH);
+
+ // get the UI state
+ String displayQuery = savedInstanceState.getString(INSTANCE_KEY_DISPLAY_QUERY);
+ int querySelStart = savedInstanceState.getInt(INSTANCE_KEY_DISPLAY_SEL_START, -1);
+ int querySelEnd = savedInstanceState.getInt(INSTANCE_KEY_DISPLAY_SEL_END, -1);
+ String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
+ int selectedElement = savedInstanceState.getInt(INSTANCE_KEY_SELECTED_ELEMENT);
+ String suggestionQuery = savedInstanceState.getString(INSTANCE_KEY_SUGGESTION_QUERY);
+
+ // show the dialog. skip any show/hide animation, we want to go fast.
+ // send the text that actually generates the suggestions here; we'll replace the display
+ // text as necessary in a moment.
+ if (!show(suggestionQuery, false, launchComponent, appSearchData, globalSearch)) {
+ // for some reason, we couldn't re-instantiate
+ return;
+ }
+
+ if (mSuggestionsAdapter != null) {
+ mSuggestionsAdapter.setNonUserQuery(true);
+ }
+ mSearchTextField.setText(displayQuery);
+ // TODO because the new query is (not) processed in another thread, we can't just
+ // take away this flag (yet). The better solution here is going to require a new API
+ // in AutoCompleteTextView which allows us to change the text w/o changing the suggestions.
+// mSuggestionsAdapter.setNonUserQuery(false);
+
+ // clean up the selection state
+ switch (selectedElement) {
+ case INSTANCE_SELECTED_BUTTON:
+ mGoButton.setEnabled(true);
+ mGoButton.setFocusable(true);
+ mGoButton.requestFocus();
+ break;
+ case INSTANCE_SELECTED_QUERY:
+ if (querySelStart >= 0 && querySelEnd >= 0) {
+ mSearchTextField.requestFocus();
+ mSearchTextField.setSelection(querySelStart, querySelEnd);
+ }
+ break;
+ default:
+ // defer selecting a list element until suggestion list appears
+ mPresetSelection = selectedElement;
+ // TODO mSearchTextField.setListSelection(selectedElement)
+ break;
+ }
+ }
+
+ /**
+ * Hook for updating layout on a rotation
+ *
+ */
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (isShowing()) {
+ // Redraw (resources may have changed)
+ updateSearchButton();
+ updateSearchBadge();
+ updateQueryHint();
+ }
+ }
+
+ /**
+ * Use SearchableInfo record (from search manager service) to preconfigure the UI in various
+ * ways.
+ */
+ private void setupSearchableInfo() {
+ if (mSearchable != null) {
+ mActivityContext = mSearchable.getActivityContext(getContext());
+
+ updateSearchButton();
+ updateSearchBadge();
+ updateQueryHint();
+ updateVoiceButton();
+
+ // In order to properly configure the input method (if one is being used), we
+ // need to let it know if we'll be providing suggestions. Although it would be
+ // difficult/expensive to know if every last detail has been configured properly, we
+ // can at least see if a suggestions provider has been configured, and use that
+ // as our trigger.
+ int inputType = mSearchable.getInputType();
+ // We only touch this if the input type is set up for text (which it almost certainly
+ // should be, in the case of search!)
+ if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
+ // The existence of a suggestions authority is the proxy for "suggestions
+ // are available here"
+ inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
+ if (mSearchable.getSuggestAuthority() != null) {
+ inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
+ }
+ }
+ mSearchTextField.setInputType(inputType);
+ mSearchTextField.setImeOptions(mSearchable.getImeOptions());
+ }
+ }
+
+ /**
+ * The list of installed packages has just changed. This means that our current context
+ * may no longer be valid. This would only happen if a package is installed/removed exactly
+ * when the search bar is open. So for now we're just going to close the search
+ * bar.
+ *
+ * Anything fancier would require some checks to see if the user's context was still valid.
+ * Which would be messier.
+ */
+ public void onPackageListChange() {
+ cancel();
+ }
+
+ /**
+ * Update the text in the search button. Note: This is deprecated functionality, for
+ * 1.0 compatibility only.
+ */
+ private void updateSearchButton() {
+ String textLabel = null;
+ Drawable iconLabel = null;
+ int textId = mSearchable.getSearchButtonText();
+ if (textId != 0) {
+ textLabel = mActivityContext.getResources().getString(textId);
+ } else {
+ iconLabel = getContext().getResources().
+ getDrawable(com.android.internal.R.drawable.ic_btn_search);
+ }
+ mGoButton.setText(textLabel);
+ mGoButton.setCompoundDrawablesWithIntrinsicBounds(iconLabel, null, null, null);
+ }
+
+ /**
+ * Setup the search "Badge" if request by mode flags.
+ */
+ private void updateSearchBadge() {
+ // assume both hidden
+ int visibility = View.GONE;
+ Drawable icon = null;
+ String text = null;
+
+ // optionally show one or the other.
+ if (mSearchable.mBadgeIcon) {
+ icon = mActivityContext.getResources().getDrawable(mSearchable.getIconId());
+ visibility = View.VISIBLE;
+ } else if (mSearchable.mBadgeLabel) {
+ text = mActivityContext.getResources().getText(mSearchable.getLabelId()).toString();
+ visibility = View.VISIBLE;
+ }
+
+ mBadgeLabel.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
+ mBadgeLabel.setText(text);
+ mBadgeLabel.setVisibility(visibility);
+ }
+
+ /**
+ * Update the hint in the query text field.
+ */
+ private void updateQueryHint() {
+ if (isShowing()) {
+ String hint = null;
+ if (mSearchable != null) {
+ int hintId = mSearchable.getHintId();
+ if (hintId != 0) {
+ hint = mActivityContext.getString(hintId);
+ }
+ }
+ mSearchTextField.setHint(hint);
+ }
+ }
+
+ /**
+ * Update the visibility of the voice button. There are actually two voice search modes,
+ * either of which will activate the button.
+ */
+ private void updateVoiceButton() {
+ int visibility = View.GONE;
+ if (mSearchable.getVoiceSearchEnabled()) {
+ Intent testIntent = null;
+ if (mSearchable.getVoiceSearchLaunchWebSearch()) {
+ testIntent = mVoiceWebSearchIntent;
+ } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
+ testIntent = mVoiceAppSearchIntent;
+ }
+ if (testIntent != null) {
+ ResolveInfo ri = getContext().getPackageManager().
+ resolveActivity(testIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (ri != null) {
+ visibility = View.VISIBLE;
+ }
+ }
+ }
+ mVoiceButton.setVisibility(visibility);
+ }
+
+ /**
+ * Listeners of various types
+ */
+
+ /**
+ * Dialog's OnKeyListener implements various search-specific functionality
+ *
+ * @param keyCode This is the keycode of the typed key, and is the same value as
+ * found in the KeyEvent parameter.
+ * @param event The complete event record for the typed key
+ *
+ * @return Return true if the event was handled here, or false if not.
+ */
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ cancel();
+ return true;
+ case KeyEvent.KEYCODE_SEARCH:
+ if (TextUtils.getTrimmedLength(mSearchTextField.getText()) != 0) {
+ launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
+ } else {
+ cancel();
+ }
+ return true;
+ default:
+ SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
+ if ((actionKey != null) && (actionKey.mQueryActionMsg != null)) {
+ launchQuerySearch(keyCode, actionKey.mQueryActionMsg);
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Callback to watch the textedit field for empty/non-empty
+ */
+ private TextWatcher mTextWatcher = new TextWatcher() {
+
+ public void beforeTextChanged(CharSequence s, int start, int
+ before, int after) { }
+
+ public void onTextChanged(CharSequence s, int start,
+ int before, int after) {
+ if (DBG_LOG_TIMING == 1) {
+ dbgLogTiming("onTextChanged()");
+ }
+ updateWidgetState();
+ // Only do suggestions if actually typed by user
+ if ((mSuggestionsAdapter != null) && !mSuggestionsAdapter.getNonUserQuery()) {
+ mPreviousSuggestionQuery = s.toString();
+ mUserQuery = mSearchTextField.getText().toString();
+ mUserQuerySelStart = mSearchTextField.getSelectionStart();
+ mUserQuerySelEnd = mSearchTextField.getSelectionEnd();
+ }
+ }
+
+ public void afterTextChanged(Editable s) { }
+ };
+
+ /**
+ * Enable/Disable the cancel button based on edit text state (any text?)
+ */
+ private void updateWidgetState() {
+ // enable the button if we have one or more non-space characters
+ boolean enabled =
+ TextUtils.getTrimmedLength(mSearchTextField.getText()) != 0;
+
+ mGoButton.setEnabled(enabled);
+ mGoButton.setFocusable(enabled);
+ }
+
+ private final static String[] ONE_LINE_FROM = {SearchManager.SUGGEST_COLUMN_TEXT_1 };
+ private final static String[] ONE_LINE_ICONS_FROM = {SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_ICON_1,
+ SearchManager.SUGGEST_COLUMN_ICON_2};
+ private final static String[] TWO_LINE_FROM = {SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_TEXT_2 };
+ private final static String[] TWO_LINE_ICONS_FROM = {SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_TEXT_2,
+ SearchManager.SUGGEST_COLUMN_ICON_1,
+ SearchManager.SUGGEST_COLUMN_ICON_2 };
+
+ private final static int[] ONE_LINE_TO = {com.android.internal.R.id.text1};
+ private final static int[] ONE_LINE_ICONS_TO = {com.android.internal.R.id.text1,
+ com.android.internal.R.id.icon1,
+ com.android.internal.R.id.icon2};
+ private final static int[] TWO_LINE_TO = {com.android.internal.R.id.text1,
+ com.android.internal.R.id.text2};
+ private final static int[] TWO_LINE_ICONS_TO = {com.android.internal.R.id.text1,
+ com.android.internal.R.id.text2,
+ com.android.internal.R.id.icon1,
+ com.android.internal.R.id.icon2};
+
+ /**
+ * Safely retrieve the suggestions cursor adapter from the ListView
+ *
+ * @param adapterView The ListView containing our adapter
+ * @result The CursorAdapter that we installed, or null if not set
+ */
+ private static CursorAdapter getSuggestionsAdapter(AdapterView<?> adapterView) {
+ CursorAdapter result = null;
+ if (adapterView != null) {
+ Object ad = adapterView.getAdapter();
+ if (ad instanceof CursorAdapter) {
+ result = (CursorAdapter) ad;
+ } else if (ad instanceof WrapperListAdapter) {
+ result = (CursorAdapter) ((WrapperListAdapter)ad).getWrappedAdapter();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * React to typing in the GO search button by refocusing to EditText.
+ * Continue typing the query.
+ */
+ View.OnKeyListener mButtonsKeyListener = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ // also guard against possible race conditions (late arrival after dismiss)
+ if (mSearchable != null) {
+ return refocusingKeyListener(v, keyCode, event);
+ }
+ return false;
+ }
+ };
+
+ /**
+ * React to a click in the GO button by launching a search.
+ */
+ View.OnClickListener mGoButtonClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ // also guard against possible race conditions (late arrival after dismiss)
+ if (mSearchable != null) {
+ launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
+ }
+ }
+ };
+
+ /**
+ * React to a click in the voice search button.
+ */
+ View.OnClickListener mVoiceButtonClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ try {
+ if (mSearchable.getVoiceSearchLaunchWebSearch()) {
+ getContext().startActivity(mVoiceWebSearchIntent);
+ dismiss();
+ } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
+ Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent);
+ getContext().startActivity(appSearchIntent);
+ dismiss();
+ }
+ } catch (ActivityNotFoundException e) {
+ // Should not happen, since we check the availability of
+ // voice search before showing the button. But just in case...
+ Log.w(LOG_TAG, "Could not find voice search activity");
+ }
+ }
+ };
+
+ /**
+ * Create and return an Intent that can launch the voice search activity, perform a specific
+ * voice transcription, and forward the results to the searchable activity.
+ *
+ * @param baseIntent The voice app search intent to start from
+ * @return A completely-configured intent ready to send to the voice search activity
+ */
+ private Intent createVoiceAppSearchIntent(Intent baseIntent) {
+ // create the necessary intent to set up a search-and-forward operation
+ // in the voice search system. We have to keep the bundle separate,
+ // because it becomes immutable once it enters the PendingIntent
+ Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
+ queryIntent.setComponent(mSearchable.mSearchActivity);
+ PendingIntent pending = PendingIntent.getActivity(
+ getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT);
+
+ // Now set up the bundle that will be inserted into the pending intent
+ // when it's time to do the search. We always build it here (even if empty)
+ // because the voice search activity will always need to insert "QUERY" into
+ // it anyway.
+ Bundle queryExtras = new Bundle();
+ if (mAppSearchData != null) {
+ queryExtras.putBundle(SearchManager.APP_DATA, mAppSearchData);
+ }
+
+ // Now build the intent to launch the voice search. Add all necessary
+ // extras to launch the voice recognizer, and then all the necessary extras
+ // to forward the results to the searchable activity
+ Intent voiceIntent = new Intent(baseIntent);
+
+ // Add all of the configuration options supplied by the searchable's metadata
+ String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
+ String prompt = null;
+ String language = null;
+ int maxResults = 1;
+ Resources resources = mActivityContext.getResources();
+ if (mSearchable.getVoiceLanguageModeId() != 0) {
+ languageModel = resources.getString(mSearchable.getVoiceLanguageModeId());
+ }
+ if (mSearchable.getVoicePromptTextId() != 0) {
+ prompt = resources.getString(mSearchable.getVoicePromptTextId());
+ }
+ if (mSearchable.getVoiceLanguageId() != 0) {
+ language = resources.getString(mSearchable.getVoiceLanguageId());
+ }
+ if (mSearchable.getVoiceMaxResults() != 0) {
+ maxResults = mSearchable.getVoiceMaxResults();
+ }
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
+
+ // Add the values that configure forwarding the results
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras);
+
+ return voiceIntent;
+ }
+
+ /**
+ * React to the user typing "enter" or other hardwired keys while typing in the search box.
+ * This handles these special keys while the edit box has focus.
+ */
+ View.OnKeyListener mTextKeyListener = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ cancel();
+ return true;
+ }
+ // also guard against possible race conditions (late arrival after dismiss)
+ if (mSearchable != null &&
+ TextUtils.getTrimmedLength(mSearchTextField.getText()) > 0) {
+ if (DBG_LOG_TIMING == 1) {
+ dbgLogTiming("doTextKey()");
+ }
+ // dispatch "typing in the list" first
+ if (mSearchTextField.isPopupShowing() &&
+ mSearchTextField.getListSelection() != ListView.INVALID_POSITION) {
+ return onSuggestionsKey(v, keyCode, event);
+ }
+ // otherwise, dispatch an "edit view" key
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_ENTER:
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ v.cancelLongPress();
+ launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ // capture the EditText state, so we can restore the user entry later
+ mUserQuery = mSearchTextField.getText().toString();
+ mUserQuerySelStart = mSearchTextField.getSelectionStart();
+ mUserQuerySelEnd = mSearchTextField.getSelectionEnd();
+ // pass through - we're just watching here
+ break;
+ default:
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
+ if ((actionKey != null) && (actionKey.mQueryActionMsg != null)) {
+ launchQuerySearch(keyCode, actionKey.mQueryActionMsg);
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ return false;
+ }
+ };
+
+ /**
+ * React to the user typing while the suggestions are focused. First, check for action
+ * keys. If not handled, try refocusing regular characters into the EditText. In this case,
+ * replace the query text (start typing fresh text).
+ */
+ private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) {
+ boolean handled = false;
+ // also guard against possible race conditions (late arrival after dismiss)
+ if (mSearchable != null) {
+ handled = doSuggestionsKey(v, keyCode, event);
+ }
+ return handled;
+ }
+
+ /**
+ * Per UI design, we're going to "steer" any typed keystrokes back into the EditText
+ * box, even if the user has navigated the focus to the dropdown or to the GO button.
+ *
+ * @param v The view into which the keystroke was typed
+ * @param keyCode keyCode of entered key
+ * @param event Full KeyEvent record of entered key
+ */
+ private boolean refocusingKeyListener(View v, int keyCode, KeyEvent event) {
+ boolean handled = false;
+
+ if (!event.isSystem() &&
+ (keyCode != KeyEvent.KEYCODE_DPAD_UP) &&
+ (keyCode != KeyEvent.KEYCODE_DPAD_DOWN) &&
+ (keyCode != KeyEvent.KEYCODE_DPAD_LEFT) &&
+ (keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) &&
+ (keyCode != KeyEvent.KEYCODE_DPAD_CENTER)) {
+ // restore focus and give key to EditText ...
+ // but don't replace the user's query
+ mLeaveJammedQueryOnRefocus = true;
+ if (mSearchTextField.requestFocus()) {
+ handled = mSearchTextField.dispatchKeyEvent(event);
+ }
+ mLeaveJammedQueryOnRefocus = false;
+ }
+ return handled;
+ }
+
+ /**
+ * Update query text based on transitions in and out of suggestions list.
+ */
+ /*
+ * TODO - figure out if this logic is required for the autocomplete text view version
+
+ OnFocusChangeListener mSuggestFocusListener = new OnFocusChangeListener() {
+ public void onFocusChange(View v, boolean hasFocus) {
+ // also guard against possible race conditions (late arrival after dismiss)
+ if (mSearchable == null) {
+ return;
+ }
+ // Update query text based on navigation in to/out of the suggestions list
+ if (hasFocus) {
+ // Entering the list view - record selection point from user's query
+ mUserQuery = mSearchTextField.getText().toString();
+ mUserQuerySelStart = mSearchTextField.getSelectionStart();
+ mUserQuerySelEnd = mSearchTextField.getSelectionEnd();
+ // then update the query to match the entered selection
+ jamSuggestionQuery(true, mSuggestionsList,
+ mSuggestionsList.getSelectedItemPosition());
+ } else {
+ // Exiting the list view
+
+ if (mSuggestionsList.getSelectedItemPosition() < 0) {
+ // Direct exit - Leave new suggestion in place (do nothing)
+ } else {
+ // Navigation exit - restore user's query text
+ if (!mLeaveJammedQueryOnRefocus) {
+ jamSuggestionQuery(false, null, -1);
+ }
+ }
+ }
+
+ }
+ };
+ */
+
+ /**
+ * This is the listener for the ACTION_CLOSE_SYSTEM_DIALOGS intent. It's an indication that
+ * we should close ourselves immediately, in order to allow a higher-priority UI to take over
+ * (e.g. phone call received).
+ */
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ cancel();
+ } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+ || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+ || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ onPackageListChange();
+ }
+ }
+ };
+
+ /**
+ * Various ways to launch searches
+ */
+
+ /**
+ * React to the user clicking the "GO" button. Hide the UI and launch a search.
+ *
+ * @param actionKey Pass a keycode if the launch was triggered by an action key. Pass
+ * KeyEvent.KEYCODE_UNKNOWN for no actionKey code.
+ * @param actionMsg Pass the suggestion-provided message if the launch was triggered by an
+ * action key. Pass null for no actionKey message.
+ */
+ private void launchQuerySearch(int actionKey, final String actionMsg) {
+ final String query = mSearchTextField.getText().toString();
+ final Bundle appData = mAppSearchData;
+ final SearchableInfo si = mSearchable; // cache briefly (dismiss() nulls it)
+ dismiss();
+ sendLaunchIntent(Intent.ACTION_SEARCH, null, query, appData, actionKey, actionMsg, si);
+ }
+
+ /**
+ * React to the user typing an action key while in the suggestions list
+ */
+ private boolean doSuggestionsKey(View v, int keyCode, KeyEvent event) {
+ // Exit early in case of race condition
+ if (mSuggestionsAdapter == null) {
+ return false;
+ }
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (DBG_LOG_TIMING == 1) {
+ dbgLogTiming("doSuggestionsKey()");
+ }
+
+ // First, check for enter or search (both of which we'll treat as a "click")
+ if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
+ int position = mSearchTextField.getListSelection();
+ return launchSuggestion(mSuggestionsAdapter, position);
+ }
+
+ // Next, check for left/right moves, which we use to "return" the user to the edit view
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+ // give "focus" to text editor, but don't restore the user's original query
+ int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ?
+ 0 : mSearchTextField.length();
+ mSearchTextField.setSelection(selPoint);
+ mSearchTextField.setListSelection(0);
+ mSearchTextField.clearListSelection();
+ return true;
+ }
+
+ // Next, check for an "up and out" move
+ if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mSearchTextField.getListSelection()) {
+ jamSuggestionQuery(false, null, -1);
+ // let ACTV complete the move
+ return false;
+ }
+
+ // Next, check for an "action key"
+ SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
+ if ((actionKey != null) &&
+ ((actionKey.mSuggestActionMsg != null) ||
+ (actionKey.mSuggestActionMsgColumn != null))) {
+ // launch suggestion using action key column
+ int position = mSearchTextField.getListSelection();
+ if (position >= 0) {
+ Cursor c = mSuggestionsAdapter.getCursor();
+ if (c.moveToPosition(position)) {
+ final String actionMsg = getActionKeyMessage(c, actionKey);
+ if (actionMsg != null && (actionMsg.length() > 0)) {
+ // shut down search bar and launch the activity
+ // cache everything we need because dismiss releases mems
+ setupSuggestionIntent(c, mSearchable);
+ final String query = mSearchTextField.getText().toString();
+ final Bundle appData = mAppSearchData;
+ SearchableInfo si = mSearchable;
+ String suggestionAction = mSuggestionAction;
+ Uri suggestionData = mSuggestionData;
+ String suggestionQuery = mSuggestionQuery;
+ dismiss();
+ sendLaunchIntent(suggestionAction, suggestionData,
+ suggestionQuery, appData,
+ keyCode, actionMsg, si);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Set or reset the user query to follow the selections in the suggestions
+ *
+ * @param jamQuery True means to set the query, false means to reset it to the user's choice
+ */
+ private void jamSuggestionQuery(boolean jamQuery, AdapterView<?> parent, int position) {
+ // quick check against race conditions
+ if (mSearchable == null) {
+ return;
+ }
+
+ mSuggestionsAdapter.setNonUserQuery(true); // disables any suggestions processing
+ if (jamQuery) {
+ CursorAdapter ca = getSuggestionsAdapter(parent);
+ Cursor c = ca.getCursor();
+ if (c.moveToPosition(position)) {
+ setupSuggestionIntent(c, mSearchable);
+ String jamText = null;
+
+ // Simple heuristic for selecting text with which to rewrite the query.
+ if (mSuggestionQuery != null) {
+ jamText = mSuggestionQuery;
+ } else if (mSearchable.mQueryRewriteFromData && (mSuggestionData != null)) {
+ jamText = mSuggestionData.toString();
+ } else if (mSearchable.mQueryRewriteFromText) {
+ try {
+ int column = c.getColumnIndexOrThrow(SearchManager.SUGGEST_COLUMN_TEXT_1);
+ jamText = c.getString(column);
+ } catch (RuntimeException e) {
+ // no work here, jamText is null
+ }
+ }
+ if (jamText != null) {
+ mSearchTextField.setText(jamText);
+ /* mSearchTextField.selectAll(); */ // this didn't work anyway in the old UI
+ // TODO this is only needed in the model where we have a selection in the ACTV
+ // and in the dropdown at the same time.
+ mSearchTextField.setSelection(jamText.length());
+ }
+ }
+ } else {
+ // reset user query
+ mSearchTextField.setText(mUserQuery);
+ try {
+ mSearchTextField.setSelection(mUserQuerySelStart, mUserQuerySelEnd);
+ } catch (IndexOutOfBoundsException e) {
+ // In case of error, just select all
+ Log.e(LOG_TAG, "Caught IndexOutOfBoundsException while setting selection. " +
+ "start=" + mUserQuerySelStart + " end=" + mUserQuerySelEnd +
+ " text=\"" + mUserQuery + "\"");
+ mSearchTextField.selectAll();
+ }
+ }
+ // TODO because the new query is (not) processed in another thread, we can't just
+ // take away this flag (yet). The better solution here is going to require a new API
+ // in AutoCompleteTextView which allows us to change the text w/o changing the suggestions.
+// mSuggestionsAdapter.setNonUserQuery(false);
+ }
+
+ /**
+ * Assemble a search intent and send it.
+ *
+ * @param action The intent to send, typically Intent.ACTION_SEARCH
+ * @param data The data for the intent
+ * @param query The user text entered (so far)
+ * @param appData The app data bundle (if supplied)
+ * @param actionKey If the intent was triggered by an action key, e.g. KEYCODE_CALL, it will
+ * be sent here. Pass KeyEvent.KEYCODE_UNKNOWN for no actionKey code.
+ * @param actionMsg If the intent was triggered by an action key, e.g. KEYCODE_CALL, the
+ * corresponding tag message will be sent here. Pass null for no actionKey message.
+ * @param si Reference to the current SearchableInfo. Passed here so it can be used even after
+ * we've called dismiss(), which attempts to null mSearchable.
+ */
+ private void sendLaunchIntent(final String action, final Uri data, final String query,
+ final Bundle appData, int actionKey, final String actionMsg, final SearchableInfo si) {
+ Intent launcher = new Intent(action);
+
+ if (query != null) {
+ launcher.putExtra(SearchManager.QUERY, query);
+ }
+
+ if (data != null) {
+ launcher.setData(data);
+ }
+
+ if (appData != null) {
+ launcher.putExtra(SearchManager.APP_DATA, appData);
+ }
+
+ // add launch info (action key, etc.)
+ if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
+ launcher.putExtra(SearchManager.ACTION_KEY, actionKey);
+ launcher.putExtra(SearchManager.ACTION_MSG, actionMsg);
+ }
+
+ // attempt to enforce security requirement (no 3rd-party intents)
+ launcher.setComponent(si.mSearchActivity);
+
+ getContext().startActivity(launcher);
+ }
+
+ /**
+ * Shared code for launching a query from a suggestion.
+ * @param ca The cursor adapter containing the suggestions
+ * @param position The suggestion we'll be launching from
+ * @return true if a successful launch, false if could not (e.g. bad position)
+ */
+ private boolean launchSuggestion(CursorAdapter ca, int position) {
+ Cursor c = ca.getCursor();
+ if ((c != null) && c.moveToPosition(position)) {
+ setupSuggestionIntent(c, mSearchable);
+
+ final Bundle appData = mAppSearchData;
+ SearchableInfo si = mSearchable;
+ String suggestionAction = mSuggestionAction;
+ Uri suggestionData = mSuggestionData;
+ String suggestionQuery = mSuggestionQuery;
+ dismiss();
+ sendLaunchIntent(suggestionAction, suggestionData, suggestionQuery, appData,
+ KeyEvent.KEYCODE_UNKNOWN, null, si);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * When a particular suggestion has been selected, perform the various lookups required
+ * to use the suggestion. This includes checking the cursor for suggestion-specific data,
+ * and/or falling back to the XML for defaults; It also creates REST style Uri data when
+ * the suggestion includes a data id.
+ *
+ * NOTE: Return values are in member variables mSuggestionAction & mSuggestionData.
+ *
+ * @param c The suggestions cursor, moved to the row of the user's selection
+ * @param si The searchable activity's info record
+ */
+ void setupSuggestionIntent(Cursor c, SearchableInfo si) {
+ try {
+ // use specific action if supplied, or default action if supplied, or fixed default
+ mSuggestionAction = null;
+ int mColumn = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
+ if (mColumn >= 0) {
+ final String action = c.getString(mColumn);
+ if (action != null) {
+ mSuggestionAction = action;
+ }
+ }
+ if (mSuggestionAction == null) {
+ mSuggestionAction = si.getSuggestIntentAction();
+ }
+ if (mSuggestionAction == null) {
+ mSuggestionAction = Intent.ACTION_SEARCH;
+ }
+
+ // use specific data if supplied, or default data if supplied
+ String data = null;
+ mColumn = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA);
+ if (mColumn >= 0) {
+ final String rowData = c.getString(mColumn);
+ if (rowData != null) {
+ data = rowData;
+ }
+ }
+ if (data == null) {
+ data = si.getSuggestIntentData();
+ }
+
+ // then, if an ID was provided, append it.
+ if (data != null) {
+ mColumn = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
+ if (mColumn >= 0) {
+ final String id = c.getString(mColumn);
+ if (id != null) {
+ data = data + "/" + Uri.encode(id);
+ }
+ }
+ }
+ mSuggestionData = (data == null) ? null : Uri.parse(data);
+
+ mSuggestionQuery = null;
+ mColumn = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY);
+ if (mColumn >= 0) {
+ final String query = c.getString(mColumn);
+ if (query != null) {
+ mSuggestionQuery = query;
+ }
+ }
+ } catch (RuntimeException e ) {
+ int rowNum;
+ try { // be really paranoid now
+ rowNum = c.getPosition();
+ } catch (RuntimeException e2 ) {
+ rowNum = -1;
+ }
+ Log.w(LOG_TAG, "Search Suggestions cursor at row " + rowNum +
+ " returned exception" + e.toString());
+ }
+ }
+
+ /**
+ * For a given suggestion and a given cursor row, get the action message. If not provided
+ * by the specific row/column, also check for a single definition (for the action key).
+ *
+ * @param c The cursor providing suggestions
+ * @param actionKey The actionkey record being examined
+ *
+ * @return Returns a string, or null if no action key message for this suggestion
+ */
+ private String getActionKeyMessage(Cursor c, final SearchableInfo.ActionKeyInfo actionKey) {
+ String result = null;
+ // check first in the cursor data, for a suggestion-specific message
+ final String column = actionKey.mSuggestActionMsgColumn;
+ if (column != null) {
+ try {
+ int colId = c.getColumnIndexOrThrow(column);
+ result = c.getString(colId);
+ } catch (RuntimeException e) {
+ // OK - result is already null
+ }
+ }
+ // If the cursor didn't give us a message, see if there's a single message defined
+ // for the actionkey (for all suggestions)
+ if (result == null) {
+ result = actionKey.mSuggestActionMsg;
+ }
+ return result;
+ }
+
+ /**
+ * Local subclass for AutoCompleteTextView
+ *
+ * This exists entirely to override the threshold method. Otherwise we just use the class
+ * as-is.
+ */
+ public static class SearchAutoComplete extends AutoCompleteTextView {
+
+ public SearchAutoComplete(Context context) {
+ super(null);
+ }
+
+ public SearchAutoComplete(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * We never allow ACTV to automatically replace the text, since we use "jamSuggestionQuery"
+ * to do that. There's no point in letting ACTV do this here, because in the search UI,
+ * as soon as we click a suggestion, we're going to start shutting things down.
+ */
+ @Override
+ public void replaceText(CharSequence text) {
+ }
+
+ /**
+ * We always return true, so that the effective threshold is "zero". This allows us
+ * to provide "null" suggestions such as "just show me some recent entries".
+ */
+ @Override
+ public boolean enoughToFilter() {
+ return true;
+ }
+ }
+
+ /**
+ * Support for AutoCompleteTextView-based suggestions
+ */
+ /**
+ * This class provides the filtering-based interface to suggestions providers.
+ * It is hardwired in a couple of places to support GoogleSearch - for example, it supports
+ * two-line suggestions, but it does not support icons.
+ */
+ private static class SuggestionsAdapter extends SimpleCursorAdapter {
+ private final String TAG = "SuggestionsAdapter";
+
+ SearchableInfo mSearchable;
+ private Resources mProviderResources;
+
+ // These private variables are shared by the filter thread and must be protected
+ private WeakReference<Cursor> mRecentCursor = new WeakReference<Cursor>(null);
+ private boolean mNonUserQuery = false;
+
+ public SuggestionsAdapter(Context context, SearchableInfo searchable) {
+ super(context, -1, null, null, null);
+ mSearchable = searchable;
+
+ // set up provider resources (gives us icons, etc.)
+ Context activityContext = mSearchable.getActivityContext(mContext);
+ Context providerContext = mSearchable.getProviderContext(mContext, activityContext);
+ mProviderResources = providerContext.getResources();
+ }
+
+ /**
+ * Set this field (temporarily!) to disable suggestions updating. This allows us
+ * to change the string in the text view without changing the suggestions list.
+ */
+ public void setNonUserQuery(boolean nonUserQuery) {
+ synchronized (this) {
+ mNonUserQuery = nonUserQuery;
+ }
+ }
+
+ public boolean getNonUserQuery() {
+ synchronized (this) {
+ return mNonUserQuery;
+ }
+ }
+
+ /**
+ * Use the search suggestions provider to obtain a live cursor. This will be called
+ * in a worker thread, so it's OK if the query is slow (e.g. round trip for suggestions).
+ * The results will be processed in the UI thread and changeCursor() will be called.
+ *
+ * In order to provide the Search Mgr functionality of seeing your query change as you
+ * scroll through the list, we have to be able to jam new text into the string without
+ * retriggering the suggestions. We do that here via the "nonUserQuery" flag. In that
+ * case we simply return the existing cursor.
+ *
+ * TODO: Dianne suggests that this should simply be promoted into an AutoCompleteTextView
+ * behavior (perhaps optionally).
+ *
+ * TODO: The "nonuserquery" logic has a race condition because it happens in another thread.
+ * This also needs to be fixed.
+ */
+ @Override
+ public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
+ String query = (constraint == null) ? "" : constraint.toString();
+ Cursor c = null;
+ synchronized (this) {
+ if (mNonUserQuery) {
+ c = mRecentCursor.get();
+ mNonUserQuery = false;
+ }
+ }
+ if (c == null) {
+ c = getSuggestions(mSearchable, query);
+ synchronized (this) {
+ mRecentCursor = new WeakReference<Cursor>(c);
+ }
+ }
+ return c;
+ }
+
+ /**
+ * Overriding changeCursor() allows us to change not only the cursor, but by sampling
+ * the cursor's columns, the actual display characteristics of the list.
+ */
+ @Override
+ public void changeCursor(Cursor c) {
+
+ // first, check for various conditions that disqualify this cursor
+ if ((c == null) || (c.getCount() == 0)) {
+ // no cursor, or cursor with no data
+ changeCursorAndColumns(null, null, null);
+ if (c != null) {
+ c.close();
+ }
+ return;
+ }
+
+ // check cursor before trying to create list views from it
+ int colId = c.getColumnIndex("_id");
+ int col1 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
+ int col2 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
+ int colIc1 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
+ int colIc2 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
+
+ boolean minimal = (colId >= 0) && (col1 >= 0);
+ boolean hasIcons = (colIc1 >= 0) && (colIc2 >= 0);
+ boolean has2Lines = col2 >= 0;
+
+ if (minimal) {
+ int layout;
+ String[] from;
+ int[] to;
+
+ if (hasIcons) {
+ if (has2Lines) {
+ layout = com.android.internal.R.layout.search_dropdown_item_icons_2line;
+ from = TWO_LINE_ICONS_FROM;
+ to = TWO_LINE_ICONS_TO;
+ } else {
+ layout = com.android.internal.R.layout.search_dropdown_item_icons_1line;
+ from = ONE_LINE_ICONS_FROM;
+ to = ONE_LINE_ICONS_TO;
+ }
+ } else {
+ if (has2Lines) {
+ layout = com.android.internal.R.layout.search_dropdown_item_2line;
+ from = TWO_LINE_FROM;
+ to = TWO_LINE_TO;
+ } else {
+ layout = com.android.internal.R.layout.search_dropdown_item_1line;
+ from = ONE_LINE_FROM;
+ to = ONE_LINE_TO;
+ }
+ }
+ // Now actually set up the cursor, columns, and the list view
+ changeCursorAndColumns(c, from, to);
+ setViewResource(layout);
+ } else {
+ // Provide some help for developers instead of just silently discarding
+ Log.w(LOG_TAG, "Suggestions cursor discarded due to missing required columns.");
+ changeCursorAndColumns(null, null, null);
+ c.close();
+ }
+ if ((colIc1 >= 0) != (colIc2 >= 0)) {
+ Log.w(LOG_TAG, "Suggestion icon column(s) discarded, must be 0 or 2 columns.");
+ }
+ }
+
+ /**
+ * Overriding this allows us to write the selected query back into the box.
+ * NOTE: This is a vastly simplified version of SearchDialog.jamQuery() and does
+ * not universally support the search API. But it is sufficient for Google Search.
+ */
+ @Override
+ public CharSequence convertToString(Cursor cursor) {
+ CharSequence result = null;
+ if (cursor != null) {
+ int column = cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY);
+ if (column >= 0) {
+ final String query = cursor.getString(column);
+ if (query != null) {
+ result = query;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get the query cursor for the search suggestions.
+ *
+ * TODO this is functionally identical to the version in SearchDialog.java. Perhaps it
+ * could be hoisted into SearchableInfo or some other shared spot.
+ *
+ * @param query The search text entered (so far)
+ * @return Returns a cursor with suggestions, or null if no suggestions
+ */
+ private Cursor getSuggestions(final SearchableInfo searchable, final String query) {
+ Cursor cursor = null;
+ if (searchable.getSuggestAuthority() != null) {
+ try {
+ StringBuilder uriStr = new StringBuilder("content://");
+ uriStr.append(searchable.getSuggestAuthority());
+
+ // if content path provided, insert it now
+ final String contentPath = searchable.getSuggestPath();
+ if (contentPath != null) {
+ uriStr.append('/');
+ uriStr.append(contentPath);
+ }
+
+ // append standard suggestion query path
+ uriStr.append('/' + SearchManager.SUGGEST_URI_PATH_QUERY);
+
+ // inject query, either as selection args or inline
+ String[] selArgs = null;
+ if (searchable.getSuggestSelection() != null) { // use selection if provided
+ selArgs = new String[] {query};
+ } else {
+ uriStr.append('/'); // no sel, use REST pattern
+ uriStr.append(Uri.encode(query));
+ }
+
+ // finally, make the query
+ cursor = mContext.getContentResolver().query(
+ Uri.parse(uriStr.toString()), null,
+ searchable.getSuggestSelection(), selArgs,
+ null);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Search Suggestions query returned exception " + e.toString());
+ cursor = null;
+ }
+ }
+
+ return cursor;
+ }
+
+ /**
+ * Overriding this allows us to affect the way that an icon is loaded. Specifically,
+ * we can be more controlling about the resource path (and allow icons to come from other
+ * packages).
+ *
+ * TODO: This is 100% identical to the version in SearchDialog.java
+ *
+ * @param v ImageView to receive an image
+ * @param value the value retrieved from the cursor
+ */
+ @Override
+ public void setViewImage(ImageView v, String value) {
+ int resID;
+ Drawable img = null;
+
+ try {
+ resID = Integer.parseInt(value);
+ if (resID != 0) {
+ img = mProviderResources.getDrawable(resID);
+ }
+ } catch (NumberFormatException nfe) {
+ // img = null;
+ } catch (NotFoundException e2) {
+ // img = null;
+ }
+
+ // finally, set the image to whatever we've gotten
+ v.setImageDrawable(img);
+ }
+
+ /**
+ * This method is overridden purely to provide a bit of protection against
+ * flaky content providers.
+ *
+ * TODO: This is 100% identical to the version in SearchDialog.java
+ *
+ * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ try {
+ return super.getView(position, convertView, parent);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Search Suggestions cursor returned exception " + e.toString());
+ // what can I return here?
+ View v = newView(mContext, mCursor, parent);
+ if (v != null) {
+ TextView tv = (TextView) v.findViewById(com.android.internal.R.id.text1);
+ tv.setText(e.toString());
+ }
+ return v;
+ }
+ }
+
+ }
+
+ /**
+ * Implements OnItemClickListener
+ */
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ // Log.d(LOG_TAG, "onItemClick() position " + position);
+ launchSuggestion(mSuggestionsAdapter, position);
+ }
+
+ /**
+ * Implements OnItemSelectedListener
+ */
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ // Log.d(LOG_TAG, "onItemSelected() position " + position);
+ jamSuggestionQuery(true, parent, position);
+ }
+
+ /**
+ * Implements OnItemSelectedListener
+ */
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Log.d(LOG_TAG, "onNothingSelected()");
+ }
+
+ /**
+ * Debugging Support
+ */
+
+ /**
+ * For debugging only, sample the millisecond clock and log it.
+ * Uses AtomicLong so we can use in multiple threads
+ */
+ private AtomicLong mLastLogTime = new AtomicLong(SystemClock.uptimeMillis());
+ private void dbgLogTiming(final String caller) {
+ long millis = SystemClock.uptimeMillis();
+ long oldTime = mLastLogTime.getAndSet(millis);
+ long delta = millis - oldTime;
+ final String report = millis + " (+" + delta + ") ticks for Search keystroke in " + caller;
+ Log.d(LOG_TAG,report);
+ }
+}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
new file mode 100644
index 0000000..c1d66f4
--- /dev/null
+++ b/core/java/android/app/SearchManager.java
@@ -0,0 +1,1385 @@
+/*
+ * 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.app;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ServiceManager;
+import android.view.KeyEvent;
+
+/**
+ * This class provides access to the system search services.
+ *
+ * <p>In practice, you won't interact with this class directly, as search
+ * services are provided through methods in {@link android.app.Activity Activity}
+ * methods and the the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
+ * {@link android.content.Intent Intent}. This class does provide a basic
+ * overview of search services and how to integrate them with your activities.
+ * If you do require direct access to the Search Manager, do not instantiate
+ * this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * context.getSystemService(Context.SEARCH_SERVICE)}.
+ *
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#DeveloperGuide">Developer Guide</a>
+ * <li><a href="#HowSearchIsInvoked">How Search Is Invoked</a>
+ * <li><a href="#QuerySearchApplications">Query-Search Applications</a>
+ * <li><a href="#FilterSearchApplications">Filter-Search Applications</a>
+ * <li><a href="#Suggestions">Search Suggestions</a>
+ * <li><a href="#ActionKeys">Action Keys</a>
+ * <li><a href="#SearchabilityMetadata">Searchability Metadata</a>
+ * <li><a href="#PassingSearchContext">Passing Search Context</a>
+ * <li><a href="#ProtectingUserPrivacy">Protecting User Privacy</a>
+ * </ol>
+ *
+ * <a name="DeveloperGuide"></a>
+ * <h3>Developer Guide</h3>
+ *
+ * <p>The ability to search for user, system, or network based data is considered to be
+ * a core user-level feature of the android platform. At any time, the user should be
+ * able to use a familiar command, button, or keystroke to invoke search, and the user
+ * should be able to search any data which is available to them. The goal is to make search
+ * appear to the user as a seamless, system-wide feature.
+ *
+ * <p>In terms of implementation, there are three broad classes of Applications:
+ * <ol>
+ * <li>Applications that are not inherently searchable</li>
+ * <li>Query-Search Applications</li>
+ * <li>Filter-Search Applications</li>
+ * </ol>
+ * <p>These categories, as well as related topics, are discussed in
+ * the sections below.
+ *
+ * <p>Even if your application is not <i>searchable</i>, it can still support the invocation of
+ * search. Please review the section <a href="#HowSearchIsInvoked">How Search Is Invoked</a>
+ * for more information on how to support this.
+ *
+ * <p>Many applications are <i>searchable</i>. These are
+ * the applications which can convert a query string into a list of results.
+ * Within this subset, applications can be grouped loosely into two families:
+ * <ul><li><i>Query Search</i> applications perform batch-mode searches - each query string is
+ * converted to a list of results.</li>
+ * <li><i>Filter Search</i> applications provide live filter-as-you-type searches.</li></ul>
+ * <p>Generally speaking, you would use query search for network-based data, and filter
+ * search for local data, but this is not a hard requirement and applications
+ * are free to use the model that fits them best (or invent a new model).
+ * <p>It should be clear that the search implementation decouples "search
+ * invocation" from "searchable". This satisfies the goal of making search appear
+ * to be "universal". The user should be able to launch any search from
+ * almost any context.
+ *
+ * <a name="HowSearchIsInvoked"></a>
+ * <h3>How Search Is Invoked</h3>
+ *
+ * <p>Unless impossible or inapplicable, all applications should support
+ * invoking the search UI. This means that when the user invokes the search command,
+ * a search UI will be presented to them. The search command is currently defined as a menu
+ * item called "Search" (with an alphabetic shortcut key of "S"), or on some devices, a dedicated
+ * search button key.
+ * <p>If your application is not inherently searchable, you can also allow the search UI
+ * to be invoked in a "web search" mode. If the user enters a search term and clicks the
+ * "Search" button, this will bring the browser to the front and will launch a web-based
+ * search. The user will be able to click the "Back" button and return to your application.
+ * <p>In general this is implemented by your activity, or the {@link android.app.Activity Activity}
+ * base class, which captures the search command and invokes the Search Manager to
+ * display and operate the search UI. You can also cause the search UI to be presented in response
+ * to user keystrokes in your activity (for example, to instantly start filter searching while
+ * viewing a list and typing any key).
+ * <p>The search UI is presented as a floating
+ * window and does not cause any change in the activity stack. If the user
+ * cancels search, the previous activity re-emerges. If the user launches a
+ * search, this will be done by sending a search {@link android.content.Intent Intent} (see below),
+ * and the normal intent-handling sequence will take place (your activity will pause,
+ * etc.)
+ * <p><b>What you need to do:</b> First, you should consider the way in which you want to
+ * handle invoking search. There are four broad (and partially overlapping) categories for
+ * you to choose from.
+ * <ul><li>You can capture the search command yourself, by including a <i>search</i>
+ * button or menu item - and invoking the search UI directly.</li>
+ * <li>You can provide a <i>type-to-search</i> feature, in which search is invoked automatically
+ * when the user enters any characters.</li>
+ * <li>Even if your application is not inherently searchable, you can allow web search,
+ * via the search key (or even via a search menu item).
+ * <li>You can disable search entirely. This should only be used in very rare circumstances,
+ * as search is a system-wide feature and users will expect it to be available in all contexts.</li>
+ * </ul>
+ *
+ * <p><b>How to define a search menu.</b> The system provides the following resources which may
+ * be useful when adding a search item to your menu:
+ * <ul><li>android.R.drawable.ic_search_category_default is an icon you can use in your menu.</li>
+ * <li>{@link #MENU_KEY SearchManager.MENU_KEY} is the recommended alphabetic shortcut.</li>
+ * </ul>
+ *
+ * <p><b>How to invoke search directly.</b> In order to invoke search directly, from a button
+ * or menu item, you can launch a generic search by calling
+ * {@link android.app.Activity#onSearchRequested onSearchRequested} as shown:
+ * <pre class="prettyprint">
+ * onSearchRequested();</pre>
+ *
+ * <p><b>How to implement type-to-search.</b> While setting up your activity, call
+ * {@link android.app.Activity#setDefaultKeyMode setDefaultKeyMode}:
+ * <pre class="prettyprint">
+ * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); // search within your activity
+ * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL); // search using platform global search</pre>
+ *
+ * <p><b>How to enable web-based search.</b> In addition to searching within your activity or
+ * application, you can also use the Search Manager to invoke a platform-global search, typically
+ * a web search. There are two ways to do this:
+ * <ul><li>You can simply define "search" within your application or activity to mean global search.
+ * This is described in more detail in the
+ * <a href="#SearchabilityMetadata">Searchability Metadata</a> section. Briefly, you will
+ * add a single meta-data entry to your manifest, declaring that the default search
+ * for your application is "*". This indicates to the system that no application-specific
+ * search activity is provided, and that it should launch web-based search instead.</li>
+ * <li>You can specify this at invocation time via default keys (see above), overriding
+ * {@link android.app.Activity#onSearchRequested}, or via a direct call to
+ * {@link android.app.Activity#startSearch}. This is most useful if you wish to provide local
+ * searchability <i>and</i> access to global search.</li></ul>
+ *
+ * <p><b>How to disable search from your activity.</b> search is a system-wide feature and users
+ * will expect it to be available in all contexts. If your UI design absolutely precludes
+ * launching search, override {@link android.app.Activity#onSearchRequested onSearchRequested}
+ * as shown:
+ * <pre class="prettyprint">
+ * &#64;Override
+ * public boolean onSearchRequested() {
+ * return false;
+ * }</pre>
+ *
+ * <p><b>Managing focus and knowing if Search is active.</b> The search UI is not a separate
+ * activity, and when the UI is invoked or dismissed, your activity will not typically be paused,
+ * resumed, or otherwise notified by the methods defined in
+ * <a href="{@docRoot}guide/topics/fundamentals.html#actlife">Application Fundamentals:
+ * Activity Lifecycle</a>. The search UI is
+ * handled in the same way as other system UI elements which may appear from time to time, such as
+ * notifications, screen locks, or other system alerts:
+ * <p>When the search UI appears, your activity will lose input focus.
+ * <p>When the search activity is dismissed, there are three possible outcomes:
+ * <ul><li>If the user simply canceled the search UI, your activity will regain input focus and
+ * proceed as before. See {@link #setOnDismissListener} and {@link #setOnCancelListener} if you
+ * required direct notification of search dialog dismissals.</li>
+ * <li>If the user launched a search, and this required switching to another activity to receive
+ * and process the search {@link android.content.Intent Intent}, your activity will receive the
+ * normal sequence of activity pause or stop notifications.</li>
+ * <li>If the user launched a search, and the current activity is the recipient of the search
+ * {@link android.content.Intent Intent}, you will receive notification via the
+ * {@link android.app.Activity#onNewIntent onNewIntent()} method.</li></ul>
+ * <p>This list is provided in order to clarify the ways in which your activities will interact with
+ * the search UI. More details on searchable activities and search intents are provided in the
+ * sections below.
+ *
+ * <a name="QuerySearchApplications"></a>
+ * <h3>Query-Search Applications</h3>
+ *
+ * <p>Query-search applications are those that take a single query (e.g. a search
+ * string) and present a set of results that may fit. Primary examples include
+ * web queries, map lookups, or email searches (with the common thread being
+ * network query dispatch). It may also be the case that certain local searches
+ * are treated this way. It's up to the application to decide.
+ *
+ * <p><b>What you need to do:</b> The following steps are necessary in order to
+ * implement query search.
+ * <ul>
+ * <li>Implement search invocation as described above. (Strictly speaking,
+ * these are decoupled, but it would make little sense to be "searchable" but not
+ * "search-invoking".)</li>
+ * <li>Your application should have an activity that takes a search string and
+ * converts it to a list of results. This could be your primary display activity
+ * or it could be a dedicated search results activity. This is your <i>searchable</i>
+ * activity and every query-search application must have one.</li>
+ * <li>In the searchable activity, in onCreate(), you must receive and handle the
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
+ * {@link android.content.Intent Intent}. The text to search (query string) for is provided by
+ * calling
+ * {@link #QUERY getStringExtra(SearchManager.QUERY)}.</li>
+ * <li>To identify and support your searchable activity, you'll need to
+ * provide an XML file providing searchability configuration parameters, a reference to that
+ * in your searchable activity's <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>
+ * entry, and an intent-filter declaring that you can
+ * receive ACTION_SEARCH intents. This is described in more detail in the
+ * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.</li>
+ * <li>Your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> also needs a metadata entry
+ * providing a global reference to the searchable activity. This is the "glue" directing the search
+ * UI, when invoked from any of your <i>other</i> activities, to use your application as the
+ * default search context. This is also described in more detail in the
+ * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.</li>
+ * <li>Finally, you may want to define your search results activity as with the
+ * {@link android.R.attr#launchMode singleTop} launchMode flag. This allows the system
+ * to launch searches from/to the same activity without creating a pile of them on the
+ * activity stack. If you do this, be sure to also override
+ * {@link android.app.Activity#onNewIntent onNewIntent} to handle the
+ * updated intents (with new queries) as they arrive.</li>
+ * </ul>
+ *
+ * <p>Code snippet showing handling of intents in your search activity:
+ * <pre class="prettyprint">
+ * &#64;Override
+ * protected void onCreate(Bundle icicle) {
+ * super.onCreate(icicle);
+ *
+ * final Intent queryIntent = getIntent();
+ * final String queryAction = queryIntent.getAction();
+ * if (Intent.ACTION_SEARCH.equals(queryAction)) {
+ * doSearchWithIntent(queryIntent);
+ * }
+ * }
+ *
+ * private void doSearchWithIntent(final Intent queryIntent) {
+ * final String queryString = queryIntent.getStringExtra(SearchManager.QUERY);
+ * doSearchWithQuery(queryString);
+ * }</pre>
+ *
+ * <a name="FilterSearchApplications"></a>
+ * <h3>Filter-Search Applications</h3>
+ *
+ * <p>Filter-search applications are those that use live text entry (e.g. keystrokes)) to
+ * display and continuously update a list of results. Primary examples include applications
+ * that use locally-stored data.
+ *
+ * <p>Filter search is not directly supported by the Search Manager. Most filter search
+ * implementations will use variants of {@link android.widget.Filterable}, such as a
+ * {@link android.widget.ListView} bound to a {@link android.widget.SimpleCursorAdapter}. However,
+ * you may find it useful to mix them together, by declaring your filtered view searchable. With
+ * this configuration, you can still present the standard search dialog in all activities
+ * within your application, but transition to a filtered search when you enter the activity
+ * and display the results.
+ *
+ * <a name="Suggestions"></a>
+ * <h3>Search Suggestions</h3>
+ *
+ * <p>A powerful feature of the Search Manager is the ability of any application to easily provide
+ * live "suggestions" in order to prompt the user. Each application implements suggestions in a
+ * different, unique, and appropriate way. Suggestions be drawn from many sources, including but
+ * not limited to:
+ * <ul>
+ * <li>Actual searchable results (e.g. names in the address book)</li>
+ * <li>Recently entered queries</li>
+ * <li>Recently viewed data or results</li>
+ * <li>Contextually appropriate queries or results</li>
+ * <li>Summaries of possible results</li>
+ * </ul>
+ *
+ * <p>Another feature of suggestions is that they can expose queries or results before the user
+ * ever visits the application. This reduces the amount of context switching required, and helps
+ * the user access their data quickly and with less context shifting. In order to provide this
+ * capability, suggestions are accessed via a
+ * {@link android.content.ContentProvider Content Provider}.
+ *
+ * <p>The primary form of suggestions is known as <i>queried suggestions</i> and is based on query
+ * text that the user has already typed. This would generally be based on partial matches in
+ * the available data. In certain situations - for example, when no query text has been typed yet -
+ * an application may also opt to provide <i>zero-query suggestions</i>.
+ * These would typically be drawn from the same data source, but because no partial query text is
+ * available, they should be weighted based on other factors - for example, most recent queries
+ * or most recent results.
+ *
+ * <p><b>Overview of how suggestions are provided.</b> When the search manager identifies a
+ * particular activity as searchable, it will check for certain metadata which indicates that
+ * there is also a source of suggestions. If suggestions are provided, the following steps are
+ * taken.
+ * <ul><li>Using formatting information found in the metadata, the user's query text (whatever
+ * has been typed so far) will be formatted into a query and sent to the suggestions
+ * {@link android.content.ContentProvider Content Provider}.</li>
+ * <li>The suggestions {@link android.content.ContentProvider Content Provider} will create a
+ * {@link android.database.Cursor Cursor} which can iterate over the possible suggestions.</li>
+ * <li>The search manager will populate a list using display data found in each row of the cursor,
+ * and display these suggestions to the user.</li>
+ * <li>If the user types another key, or changes the query in any way, the above steps are repeated
+ * and the suggestions list is updated or repopulated.</li>
+ * <li>If the user clicks or touches the "GO" button, the suggestions are ignored and the search is
+ * launched using the normal {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} type of
+ * {@link android.content.Intent Intent}.</li>
+ * <li>If the user uses the directional controls to navigate the focus into the suggestions list,
+ * the query text will be updated while the user navigates from suggestion to suggestion. The user
+ * can then click or touch the updated query and edit it further. If the user navigates back to
+ * the edit field, the original typed query is restored.</li>
+ * <li>If the user clicks or touches a particular suggestion, then a combination of data from the
+ * cursor and
+ * values found in the metadata are used to synthesize an Intent and send it to the application.
+ * Depending on the design of the activity and the way it implements search, this might be a
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} (in order to launch a query), or it
+ * might be a {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}, in order to proceed directly
+ * to display of specific data.</li>
+ * </ul>
+ *
+ * <p><b>Simple Recent-Query-Based Suggestions.</b> The Android framework provides a simple Search
+ * Suggestions provider, which simply records and replays recent queries. For many applications,
+ * this will be sufficient. The basic steps you will need to
+ * do, in order to use the built-in recent queries suggestions provider, are as follows:
+ * <ul>
+ * <li>Implement and test query search, as described in the previous sections.</li>
+ * <li>Create a Provider within your application by extending
+ * {@link android.content.SearchRecentSuggestionsProvider}.</li>
+ * <li>Create a manifest entry describing your provider.</li>
+ * <li>Update your searchable activity's XML configuration file with information about your
+ * provider.</li>
+ * <li>In your searchable activities, capture any user-generated queries and record them
+ * for future searches by calling {@link android.provider.SearchRecentSuggestions#saveRecentQuery}.
+ * </li>
+ * </ul>
+ * <p>For complete implementation details, please refer to
+ * {@link android.content.SearchRecentSuggestionsProvider}. The rest of the information in this
+ * section should not be necessary, as it refers to custom suggestions providers.
+ *
+ * <p><b>Creating a Customized Suggestions Provider:</b> In order to create more sophisticated
+ * suggestion providers, you'll need to take the following steps:
+ * <ul>
+ * <li>Implement and test query search, as described in the previous sections.</li>
+ * <li>Decide how you wish to <i>receive</i> suggestions. Just like queries that the user enters,
+ * suggestions will be delivered to your searchable activity as
+ * {@link android.content.Intent Intent} messages; Unlike simple queries, you have quite a bit of
+ * flexibility in forming those intents. A query search application will probably
+ * wish to continue receiving the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
+ * {@link android.content.Intent Intent}, which will launch a query search using query text as
+ * provided by the suggestion. A filter search application will probably wish to
+ * receive the {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}
+ * {@link android.content.Intent Intent}, which will take the user directly to a selected entry.
+ * Other interesting suggestions, including hybrids, are possible, and the suggestion provider
+ * can easily mix-and-match results to provide a richer set of suggestions for the user. Finally,
+ * you'll need to update your searchable activity (or other activities) to receive the intents
+ * as you've defined them.</li>
+ * <li>Implement a Content Provider that provides suggestions. If you already have one, and it
+ * has access to your suggestions data. If not, you'll have to create one.
+ * You'll also provide information about your Content Provider in your
+ * package's <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</li>
+ * <li>Update your searchable activity's XML configuration file. There are two categories of
+ * information used for suggestions:
+ * <ul><li>The first is (required) data that the search manager will
+ * use to format the queries which are sent to the Content Provider.</li>
+ * <li>The second is (optional) parameters to configure structure
+ * if intents generated by suggestions.</li></li>
+ * </ul>
+ * </ul>
+ *
+ * <p><b>Configuring your Content Provider to Receive Suggestion Queries.</b> The basic job of
+ * a search suggestions {@link android.content.ContentProvider Content Provider} is to provide
+ * "live" (while-you-type) conversion of the user's query text into a set of zero or more
+ * suggestions. Each application is free to define the conversion, and as described above there are
+ * many possible solutions. This section simply defines how to communicate with the suggestion
+ * provider.
+ *
+ * <p>The Search Manager must first determine if your package provides suggestions. This is done
+ * by examination of your searchable meta-data XML file. The android:searchSuggestAuthority
+ * attribute, if provided, is the signal to obtain & display suggestions.
+ *
+ * <p>Every query includes a Uri, and the Search Manager will format the Uri as shown:
+ * <p><pre class="prettyprint">
+ * content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY</pre>
+ *
+ * <p>Your Content Provider can receive the query text in one of two ways.
+ * <ul>
+ * <li><b>Query provided as a selection argument.</b> If you define the attribute value
+ * android:searchSuggestSelection and include a string, this string will be passed as the
+ * <i>selection</i> parameter to your Content Provider's query function. You must define a single
+ * selection argument, using the '?' character. The user's query text will be passed to you
+ * as the first element of the selection arguments array.</li>
+ * <li><b>Query provided with Data Uri.</b> If you <i>do not</i> define the attribute value
+ * android:searchSuggestSelection, then the Search Manager will append another "/" followed by
+ * the user's query to the query Uri. The query will be encoding using Uri encoding rules - don't
+ * forget to decode it. (See {@link android.net.Uri#getPathSegments} and
+ * {@link android.net.Uri#getLastPathSegment} for helpful utilities you can use here.)</li>
+ * </ul>
+ *
+ * <p><b>Handling empty queries.</b> Your application should handle the "empty query"
+ * (no user text entered) case properly, and generate useful suggestions in this case. There are a
+ * number of ways to do this; Two are outlined here:
+ * <ul><li>For a simple filter search of local data, you could simply present the entire dataset,
+ * unfiltered. (example: People)</li>
+ * <li>For a query search, you could simply present the most recent queries. This allows the user
+ * to quickly repeat a recent search.</li></ul>
+ *
+ * <p><b>The Format of Individual Suggestions.</b> Your suggestions are communicated back to the
+ * Search Manager by way of a {@link android.database.Cursor Cursor}. The Search Manager will
+ * usually pass a null Projection, which means that your provider can simply return all appropriate
+ * columns for each suggestion. The columns currently defined are:
+ *
+ * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
+ *
+ * <thead>
+ * <tr><th>Column Name</th> <th>Description</th> <th>Required?</th></tr>
+ * </thead>
+ *
+ * <tbody>
+ * <tr><th>{@link #SUGGEST_COLUMN_FORMAT}</th>
+ * <td><i>Unused - can be null.</i></td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>{@link #SUGGEST_COLUMN_TEXT_1}</th>
+ * <td>This is the line of text that will be presented to the user as the suggestion.</td>
+ * <td align="center">Yes</td>
+ * </tr>
+ *
+ * <tr><th>{@link #SUGGEST_COLUMN_TEXT_2}</th>
+ * <td>If your cursor includes this column, then all suggestions will be provided in a
+ * two-line format. The data in this column will be displayed as a second, smaller
+ * line of text below the primary suggestion, or it can be null or empty to indicate no
+ * text in this row's suggestion.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>{@link #SUGGEST_COLUMN_ICON_1}</th>
+ * <td>If your cursor includes this column, then all suggestions will be provided in an
+ * icons+text format. This value should be a reference (resource ID) of the icon to
+ * draw on the left side, or it can be null or zero to indicate no icon in this row.
+ * You must provide both cursor columns, or neither.
+ * </td>
+ * <td align="center">No, but required if you also have {@link #SUGGEST_COLUMN_ICON_2}</td>
+ * </tr>
+ *
+ * <tr><th>{@link #SUGGEST_COLUMN_ICON_2}</th>
+ * <td>If your cursor includes this column, then all suggestions will be provided in an
+ * icons+text format. This value should be a reference (resource ID) of the icon to
+ * draw on the right side, or it can be null or zero to indicate no icon in this row.
+ * You must provide both cursor columns, or neither.
+ * </td>
+ * <td align="center">No, but required if you also have {@link #SUGGEST_COLUMN_ICON_1}</td>
+ * </tr>
+ *
+ * <tr><th>{@link #SUGGEST_COLUMN_INTENT_ACTION}</th>
+ * <td>If this column exists <i>and</i> this element exists at the given row, this is the
+ * action that will be used when forming the suggestion's intent. If the element is
+ * not provided, the action will be taken from the android:searchSuggestIntentAction
+ * field in your XML metadata. <i>At least one of these must be present for the
+ * suggestion to generate an intent.</i> Note: If your action is the same for all
+ * suggestions, it is more efficient to specify it using XML metadata and omit it from
+ * the cursor.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>{@link #SUGGEST_COLUMN_INTENT_DATA}</th>
+ * <td>If this column exists <i>and</i> this element exists at the given row, this is the
+ * data that will be used when forming the suggestion's intent. If the element is not
+ * provided, the data will be taken from the android:searchSuggestIntentData field in
+ * your XML metadata. If neither source is provided, the Intent's data field will be
+ * null. Note: If your data is the same for all suggestions, or can be described
+ * using a constant part and a specific ID, it is more efficient to specify it using
+ * XML metadata and omit it from the cursor.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>{@link #SUGGEST_COLUMN_INTENT_DATA_ID}</th>
+ * <td>If this column exists <i>and</i> this element exists at the given row, then "/" and
+ * this value will be appended to the data field in the Intent. This should only be
+ * used if the data field has already been set to an appropriate base string.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>{@link #SUGGEST_COLUMN_QUERY}</th>
+ * <td>If this column exists <i>and</i> this element exists at the given row, this is the
+ * data that will be used when forming the suggestion's query.</td>
+ * <td align="center">Required if suggestion's action is
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</td>
+ * </tr>
+ *
+ * <tr><th><i>Other Columns</i></th>
+ * <td>Finally, if you have defined any <a href="#ActionKeys">Action Keys</a> and you wish
+ * for them to have suggestion-specific definitions, you'll need to define one
+ * additional column per action key. The action key will only trigger if the
+ * currently-selection suggestion has a non-empty string in the corresponding column.
+ * See the section on <a href="#ActionKeys">Action Keys</a> for additional details and
+ * implementation steps.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * </tbody>
+ * </table>
+ *
+ * <p>Clearly there are quite a few permutations of your suggestion data, but in the next section
+ * we'll look at a few simple combinations that you'll select from.
+ *
+ * <p><b>The Format Of Intents Sent By Search Suggestions.</b> Although there are many ways to
+ * configure these intents, this document will provide specific information on just a few of them.
+ * <ul><li><b>Launch a query.</b> In this model, each suggestion represents a query that your
+ * searchable activity can perform, and the {@link android.content.Intent Intent} will be formatted
+ * exactly like those sent when the user enters query text and clicks the "GO" button:
+ * <ul>
+ * <li><b>Action:</b> {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} provided
+ * using your XML metadata (android:searchSuggestIntentAction).</li>
+ * <li><b>Data:</b> empty (not used).</li>
+ * <li><b>Query:</b> query text supplied by the cursor.</li>
+ * </ul>
+ * </li>
+ * <li><b>Go directly to a result, using a complete Data Uri.</b> In this model, the user will be
+ * taken directly to a specific result.
+ * <ul>
+ * <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li>
+ * <li><b>Data:</b> a complete Uri, supplied by the cursor, that identifies the desired data.</li>
+ * <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li>
+ * </ul>
+ * </li>
+ * <li><b>Go directly to a result, using a synthesized Data Uri.</b> This has the same result
+ * as the previous suggestion, but provides the Data Uri in a different way.
+ * <ul>
+ * <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li>
+ * <li><b>Data:</b> The search manager will assemble a Data Uri using the following elements:
+ * a Uri fragment provided in your XML metadata (android:searchSuggestIntentData), followed by
+ * a single "/", followed by the value found in the {@link #SUGGEST_COLUMN_INTENT_DATA_ID}
+ * entry in your cursor.</li>
+ * <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <p>This list is not meant to be exhaustive. Applications should feel free to define other types
+ * of suggestions. For example, you could reduce long lists of results to summaries, and use one
+ * of the above intents (or one of your own) with specially formatted Data Uri's to display more
+ * detailed results. Or you could display textual shortcuts as suggestions, but launch a display
+ * in a more data-appropriate format such as media artwork.
+ *
+ * <p><b>Suggestion Rewriting.</b> If the user navigates through the suggestions list, the UI
+ * may temporarily rewrite the user's query with a query that matches the currently selected
+ * suggestion. This enables the user to see what query is being suggested, and also allows the user
+ * to click or touch in the entry EditText element and make further edits to the query before
+ * dispatching it. In order to perform this correctly, the Search UI needs to know exactly what
+ * text to rewrite the query with.
+ *
+ * <p>For each suggestion, the following logic is used to select a new query string:
+ * <ul><li>If the suggestion provides an explicit value in the {@link #SUGGEST_COLUMN_QUERY}
+ * column, this value will be used.</li>
+ * <li>If the metadata includes the queryRewriteFromData flag, and the suggestion provides an
+ * explicit value for the intent Data field, this Uri will be used. Note that this should only be
+ * used with Uri's that are intended to be user-visible, such as HTTP. Internal Uri schemes should
+ * not be used in this way.</li>
+ * <li>If the metadata includes the queryRewriteFromText flag, the text in
+ * {@link #SUGGEST_COLUMN_TEXT_1} will be used. This should be used for suggestions in which no
+ * query text is provided and the SUGGEST_COLUMN_INTENT_DATA values are not suitable for user
+ * inspection and editing.</li></ul>
+ *
+ * <a name="ActionKeys"></a>
+ * <h3>Action Keys</h3>
+ *
+ * <p>Searchable activities may also wish to provide shortcuts based on the various action keys
+ * available on the device. The most basic example of this is the contacts app, which enables the
+ * green "dial" key for quick access during searching. Not all action keys are available on
+ * every device, and not all are allowed to be overriden in this way. (For example, the "Home"
+ * key must always return to the home screen, with no exceptions.)
+ *
+ * <p>In order to define action keys for your searchable application, you must do two things.
+ *
+ * <ul>
+ * <li>You'll add one or more <i>actionkey</i> elements to your searchable metadata configuration
+ * file. Each element defines one of the keycodes you are interested in,
+ * defines the conditions under which they are sent, and provides details
+ * on how to communicate the action key event back to your searchable activity.</li>
+ * <li>In your broadcast receiver, if you wish, you can check for action keys by checking the
+ * extras field of the {@link android.content.Intent Intent}.</li>
+ * </ul>
+ *
+ * <p><b>Updating metadata.</b> For each keycode of interest, you must add an &lt;actionkey&gt;
+ * element. Within this element you must define two or three attributes. The first attribute,
+ * &lt;android:keycode&gt;, is required; It is the key code of the action key event, as defined in
+ * {@link android.view.KeyEvent}. The remaining two attributes define the value of the actionkey's
+ * <i>message</i>, which will be passed to your searchable activity in the
+ * {@link android.content.Intent Intent} (see below for more details). Although each of these
+ * attributes is optional, you must define one or both for the action key to have any effect.
+ * &lt;android:queryActionMsg&gt; provides the message that will be sent if the action key is
+ * pressed while the user is simply entering query text. &lt;android:suggestActionMsgColumn&gt;
+ * is used when action keys are tied to specific suggestions. This attribute provides the name
+ * of a <i>column</i> in your suggestion cursor; The individual suggestion, in that column,
+ * provides the message. (If the cell is empty or null, that suggestion will not work with that
+ * action key.)
+ * <p>See the <a href="#SearchabilityMetadata">Searchability Metadata</a> section for more details
+ * and examples.
+ *
+ * <p><b>Receiving Action Keys</b> Intents launched by action keys will be specially marked
+ * using a combination of values. This enables your searchable application to examine the intent,
+ * if necessary, and perform special processing. For example, clicking a suggested contact might
+ * simply display them; Selecting a suggested contact and clicking the dial button might
+ * immediately call them.
+ *
+ * <p>When a search {@link android.content.Intent Intent} is launched by an action key, two values
+ * will be added to the extras field.
+ * <ul>
+ * <li>To examine the key code, use {@link android.content.Intent#getIntExtra
+ * getIntExtra(SearchManager.ACTION_KEY)}.</li>
+ * <li>To examine the message string, use {@link android.content.Intent#getStringExtra
+ * getStringExtra(SearchManager.ACTION_MSG)}</li>
+ * </ul>
+ *
+ * <a name="SearchabilityMetadata"></a>
+ * <h3>Searchability Metadata</h3>
+ *
+ * <p>Every activity that is searchable must provide a small amount of additional information
+ * in order to properly configure the search system. This controls the way that your search
+ * is presented to the user, and controls for the various modalities described previously.
+ *
+ * <p>If your application is not searchable,
+ * then you do not need to provide any search metadata, and you can skip the rest of this section.
+ * When this search metadata cannot be found, the search manager will assume that the activity
+ * does not implement search. (Note: to implement web-based search, you will need to add
+ * the android.app.default_searchable metadata to your manifest, as shown below.)
+ *
+ * <p>Values you supply in metadata apply only to each local searchable activity. Each
+ * searchable activity can define a completely unique search experience relevant to its own
+ * capabilities and user experience requirements, and a single application can even define multiple
+ * searchable activities.
+ *
+ * <p><b>Metadata for searchable activity.</b> As with your search implementations described
+ * above, you must first identify which of your activities is searchable. In the
+ * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry for this activity, you must
+ * provide two elements:
+ * <ul><li>An intent-filter specifying that you can receive and process the
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} {@link android.content.Intent Intent}.
+ * </li>
+ * <li>A reference to a small XML file (typically called "searchable.xml") which contains the
+ * remaining configuration information for how your application implements search.</li></ul>
+ *
+ * <p>Here is a snippet showing the necessary elements in the
+ * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry for your searchable activity.
+ * <pre class="prettyprint">
+ * &lt;!-- Search Activity - searchable --&gt;
+ * &lt;activity android:name="MySearchActivity"
+ * android:label="Search"
+ * android:launchMode="singleTop"&gt;
+ * &lt;intent-filter&gt;
+ * &lt;action android:name="android.intent.action.SEARCH" /&gt;
+ * &lt;category android:name="android.intent.category.DEFAULT" /&gt;
+ * &lt;/intent-filter&gt;
+ * &lt;meta-data android:name="android.app.searchable"
+ * android:resource="@xml/searchable" /&gt;
+ * &lt;/activity&gt;</pre>
+ *
+ * <p>Next, you must provide the rest of the searchability configuration in
+ * the small XML file, stored in the ../xml/ folder in your build. The XML file is a
+ * simple enumeration of the search configuration parameters for searching within this activity,
+ * application, or package. Here is a sample XML file (named searchable.xml, for use with
+ * the above manifest) for a query-search activity.
+ *
+ * <pre class="prettyprint">
+ * &lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:label="@string/search_label"
+ * android:hint="@string/search_hint" &gt;
+ * &lt;/searchable&gt;</pre>
+ *
+ * <p>Note that all user-visible strings <i>must</i> be provided in the form of "@string"
+ * references. Hard-coded strings, which cannot be localized, will not work properly in search
+ * metadata.
+ *
+ * <p>Attributes you can set in search metadata:
+ * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
+ *
+ * <thead>
+ * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
+ * </thead>
+ *
+ * <tbody>
+ * <tr><th>android:label</th>
+ * <td>This is the name for your application that will be presented to the user in a
+ * list of search targets, or in the search box as a label.</td>
+ * <td align="center">Yes</td>
+ * </tr>
+ *
+ * <tr><th>android:icon</th>
+ * <td>If provided, this icon will be used <i>in place</i> of the label string. This
+ * is provided in order to present logos or other non-textual banners.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>android:hint</th>
+ * <td>This is the text to display in the search text field when no user text has been
+ * entered.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>android:searchButtonText</th>
+ * <td>If provided, this text will replace the default text in the "Search" button.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>android:searchMode</th>
+ * <td>If provided and non-zero, sets additional modes for control of the search
+ * presentation. The following mode bits are defined:
+ * <table border="2" align="center" frame="hsides" rules="rows">
+ * <tbody>
+ * <tr><th>showSearchLabelAsBadge</th>
+ * <td>If set, this flag enables the display of the search target (label)
+ * within the search bar. If this flag and showSearchIconAsBadge
+ * (see below) are both not set, no badge will be shown.</td>
+ * </tr>
+ * <tr><th>showSearchIconAsBadge</th>
+ * <td>If set, this flag enables the display of the search target (icon) within
+ * the search bar. If this flag and showSearchLabelAsBadge
+ * (see above) are both not set, no badge will be shown. If both flags
+ * are set, showSearchIconAsBadge has precedence and the icon will be
+ * shown.</td>
+ * </tr>
+ * <tr><th>queryRewriteFromData</th>
+ * <td>If set, this flag causes the suggestion column SUGGEST_COLUMN_INTENT_DATA
+ * to be considered as the text for suggestion query rewriting. This should
+ * only be used when the values in SUGGEST_COLUMN_INTENT_DATA are suitable
+ * for user inspection and editing - typically, HTTP/HTTPS Uri's.</td>
+ * </tr>
+ * <tr><th>queryRewriteFromText</th>
+ * <td>If set, this flag causes the suggestion column SUGGEST_COLUMN_TEXT_1 to
+ * be considered as the text for suggestion query rewriting. This should
+ * be used for suggestions in which no query text is provided and the
+ * SUGGEST_COLUMN_INTENT_DATA values are not suitable for user inspection
+ * and editing.</td>
+ * </tr>
+ * </tbody>
+ * </table></td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>android:inputType</th>
+ * <td>If provided, supplies a hint about the type of search text the user will be
+ * entering. For most searches, in which free form text is expected, this attribute
+ * need not be provided. Suitable values for this attribute are described in the
+ * <a href="../R.attr.html#inputType">inputType</a> attribute.</td>
+ * <td align="center">No</td>
+ * </tr>
+ * <tr><th>android:imeOptions</th>
+ * <td>If provided, supplies additional options for the input method.
+ * For most searches, in which free form text is expected, this attribute
+ * need not be provided, and will default to "actionSearch".
+ * Suitable values for this attribute are described in the
+ * <a href="../R.attr.html#imeOptions">imeOptions</a> attribute.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * </tbody>
+ * </table>
+ *
+ * <p><b>Styleable Resources in your Metadata.</b> It's possible to provide alternate strings
+ * for your searchable application, in order to provide localization and/or to better visual
+ * presentation on different device configurations. Each searchable activity has a single XML
+ * metadata file, but any resource references can be replaced at runtime based on device
+ * configuration, language setting, and other system inputs.
+ *
+ * <p>A concrete example is the "hint" text you supply using the android:searchHint attribute.
+ * In portrait mode you'll have less screen space and may need to provide a shorter string, but
+ * in landscape mode you can provide a longer, more descriptive hint. To do this, you'll need to
+ * define two or more strings.xml files, in the following directories:
+ * <ul><li>.../res/values-land/strings.xml</li>
+ * <li>.../res/values-port/strings.xml</li>
+ * <li>.../res/values/strings.xml</li></ul>
+ *
+ * <p>For more complete documentation on this capability, see
+ * <a href="{@docRoot}guide/topics/resources/resources-i18n.html#AlternateResources">Resources and
+ * Internationalization: Alternate Resources</a>.
+ *
+ * <p><b>Metadata for non-searchable activities.</b> Activities which are part of a searchable
+ * application, but don't implement search itself, require a bit of "glue" in order to cause
+ * them to invoke search using your searchable activity as their primary context. If this is not
+ * provided, then searches from these activities will use the system default search context.
+ *
+ * <p>The simplest way to specify this is to add a <i>search reference</i> element to the
+ * application entry in the <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> file.
+ * The value of this reference can be either of:
+ * <ul><li>The name of your searchable activity.
+ * It is typically prefixed by '.' to indicate that it's in the same package.</li>
+ * <li>A "*" indicates that the system may select a default searchable activity, in which
+ * case it will typically select web-based search.</li>
+ * </ul>
+ *
+ * <p>Here is a snippet showing the necessary addition to the manifest entry for your
+ * non-searchable activities.
+ * <pre class="prettyprint">
+ * &lt;application&gt;
+ * &lt;meta-data android:name="android.app.default_searchable"
+ * android:value=".MySearchActivity" /&gt;
+ *
+ * &lt;!-- followed by activities, providers, etc... --&gt;
+ * &lt;/application&gt;</pre>
+ *
+ * <p>You can also specify android.app.default_searchable on a per-activity basis, by including
+ * the meta-data element (as shown above) in one or more activity sections. If found, these will
+ * override the reference in the application section. The only reason to configure your application
+ * this way would be if you wish to partition it into separate sections with different search
+ * behaviors; Otherwise this configuration is not recommended.
+ *
+ * <p><b>Additional Metadata for search suggestions.</b> If you have defined a content provider
+ * to generate search suggestions, you'll need to publish it to the system, and you'll need to
+ * provide a bit of additional XML metadata in order to configure communications with it.
+ *
+ * <p>First, in your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>, you'll add the
+ * following lines.
+ * <pre class="prettyprint">
+ * &lt;!-- Content provider for search suggestions --&gt;
+ * &lt;provider android:name="YourSuggestionProviderClass"
+ * android:authorities="your.suggestion.authority" /&gt;</pre>
+ *
+ * <p>Next, you'll add a few lines to your XML metadata file, as shown:
+ * <pre class="prettyprint">
+ * &lt;!-- Required attribute for any suggestions provider --&gt;
+ * android:searchSuggestAuthority="your.suggestion.authority"
+ *
+ * &lt;!-- Optional attribute for configuring queries --&gt;
+ * android:searchSuggestSelection="field =?"
+ *
+ * &lt;!-- Optional attributes for configuring intent construction --&gt;
+ * android:searchSuggestIntentAction="intent action string"
+ * android:searchSuggestIntentData="intent data Uri" /&gt;</pre>
+ *
+ * <p>Elements of search metadata that support suggestions:
+ * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
+ *
+ * <thead>
+ * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
+ * </thead>
+ *
+ * <tbody>
+ * <tr><th>android:searchSuggestAuthority</th>
+ * <td>This value must match the authority string provided in the <i>provider</i> section
+ * of your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</td>
+ * <td align="center">Yes</td>
+ * </tr>
+ *
+ * <tr><th>android:searchSuggestPath</th>
+ * <td>If provided, this will be inserted in the suggestions query Uri, after the authority
+ * you have provide but before the standard suggestions path. This is only required if
+ * you have a single content provider issuing different types of suggestions (e.g. for
+ * different data types) and you need a way to disambiguate the suggestions queries
+ * when they are received.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>android:searchSuggestSelection</th>
+ * <td>If provided, this value will be passed into your query function as the
+ * <i>selection</i> parameter. Typically this will be a WHERE clause for your database,
+ * and will contain a single question mark, which represents the actual query string
+ * that has been typed by the user. However, you can also use any non-null value
+ * to simply trigger the delivery of the query text (via selection arguments), and then
+ * use the query text in any way appropriate for your provider (ignoring the actual
+ * text of the selection parameter.)</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>android:searchSuggestIntentAction</th>
+ * <td>If provided, and not overridden by the selected suggestion, this value will be
+ * placed in the action field of the {@link android.content.Intent Intent} when the
+ * user clicks a suggestion.</td>
+ * <td align="center">No</td>
+ *
+ * <tr><th>android:searchSuggestIntentData</th>
+ * <td>If provided, and not overridden by the selected suggestion, this value will be
+ * placed in the data field of the {@link android.content.Intent Intent} when the user
+ * clicks a suggestion.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * </tbody>
+ * </table>
+ *
+ * <p><b>Additional Metadata for search action keys.</b> For each action key that you would like to
+ * define, you'll need to add an additional element defining that key, and using the attributes
+ * discussed in <a href="#ActionKeys">Action Keys</a>. A simple example is shown here:
+ *
+ * <pre class="prettyprint">&lt;actionkey
+ * android:keycode="KEYCODE_CALL"
+ * android:queryActionMsg="call"
+ * android:suggestActionMsg="call"
+ * android:suggestActionMsgColumn="call_column" /&gt;</pre>
+ *
+ * <p>Elements of search metadata that support search action keys. Note that although each of the
+ * action message elements are marked as <i>optional</i>, at least one must be present for the
+ * action key to have any effect.
+ *
+ * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
+ *
+ * <thead>
+ * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
+ * </thead>
+ *
+ * <tbody>
+ * <tr><th>android:keycode</th>
+ * <td>This attribute denotes the action key you wish to respond to. Note that not
+ * all action keys are actually supported using this mechanism, as many of them are
+ * used for typing, navigation, or system functions. This will be added to the
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to
+ * your searchable activity. To examine the key code, use
+ * {@link android.content.Intent#getIntExtra getIntExtra(SearchManager.ACTION_KEY)}.
+ * <p>Note, in addition to the keycode, you must also provide one or more of the action
+ * specifier attributes.</td>
+ * <td align="center">Yes</td>
+ * </tr>
+ *
+ * <tr><th>android:queryActionMsg</th>
+ * <td>If you wish to handle an action key during normal search query entry, you
+ * must define an action string here. This will be added to the
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to your
+ * searchable activity. To examine the string, use
+ * {@link android.content.Intent#getStringExtra
+ * getStringExtra(SearchManager.ACTION_MSG)}.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>android:suggestActionMsg</th>
+ * <td>If you wish to handle an action key while a suggestion is being displayed <i>and
+ * selected</i>, there are two ways to handle this. If <i>all</i> of your suggestions
+ * can handle the action key, you can simply define the action message using this
+ * attribute. This will be added to the
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to
+ * your searchable activity. To examine the string, use
+ * {@link android.content.Intent#getStringExtra
+ * getStringExtra(SearchManager.ACTION_MSG)}.</td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * <tr><th>android:suggestActionMsgColumn</th>
+ * <td>If you wish to handle an action key while a suggestion is being displayed <i>and
+ * selected</i>, but you do not wish to enable this action key for every suggestion,
+ * then you can use this attribute to control it on a suggestion-by-suggestion basis.
+ * First, you must define a column (and name it here) where your suggestions will
+ * include the action string. Then, in your content provider, you must provide this
+ * column, and when desired, provide data in this column.
+ * The search manager will look at your suggestion cursor, using the string
+ * provided here in order to select a column, and will use that to select a string from
+ * the cursor. That string will be added to the
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to
+ * your searchable activity. To examine the string, use
+ * {@link android.content.Intent#getStringExtra
+ * getStringExtra(SearchManager.ACTION_MSG)}. <i>If the data does not exist for the
+ * selection suggestion, the action key will be ignored.</i></td>
+ * <td align="center">No</td>
+ * </tr>
+ *
+ * </tbody>
+ * </table>
+ *
+ * <a name="PassingSearchContext"></a>
+ * <h3>Passing Search Context</h3>
+ *
+ * <p>In order to improve search experience, an application may wish to specify
+ * additional data along with the search, such as local history or context. For
+ * example, a maps search would be improved by including the current location.
+ * In order to simplify the structure of your activities, this can be done using
+ * the search manager.
+ *
+ * <p>Any data can be provided at the time the search is launched, as long as it
+ * can be stored in a {@link android.os.Bundle Bundle} object.
+ *
+ * <p>To pass application data into the Search Manager, you'll need to override
+ * {@link android.app.Activity#onSearchRequested onSearchRequested} as follows:
+ *
+ * <pre class="prettyprint">
+ * &#64;Override
+ * public boolean onSearchRequested() {
+ * Bundle appData = new Bundle();
+ * appData.put...();
+ * appData.put...();
+ * startSearch(null, false, appData);
+ * return true;
+ * }</pre>
+ *
+ * <p>To receive application data from the Search Manager, you'll extract it from
+ * the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
+ * {@link android.content.Intent Intent} as follows:
+ *
+ * <pre class="prettyprint">
+ * final Bundle appData = queryIntent.getBundleExtra(SearchManager.APP_DATA);
+ * if (appData != null) {
+ * appData.get...();
+ * appData.get...();
+ * }</pre>
+ *
+ * <a name="ProtectingUserPrivacy"></a>
+ * <h3>Protecting User Privacy</h3>
+ *
+ * <p>Many users consider their activities on the phone, including searches, to be private
+ * information. Applications that implement search should take steps to protect users' privacy
+ * wherever possible. This section covers two areas of concern, but you should consider your search
+ * design carefully and take any additional steps necessary.
+ *
+ * <p><b>Don't send personal information to servers, and if you do, don't log it.</b>
+ * "Personal information" is information that can personally identify your users, such as name,
+ * email address or billing information, or other data which can be reasonably linked to such
+ * information. If your application implements search with the assistance of a server, try to
+ * avoid sending personal information with your searches. For example, if you are searching for
+ * businesses near a zip code, you don't need to send the user ID as well - just send the zip code
+ * to the server. If you do need to send personal information, you should take steps to avoid
+ * logging it. If you must log it, you should protect that data very carefully, and erase it as
+ * soon as possible.
+ *
+ * <p><b>Provide the user with a way to clear their search history.</b> The Search Manager helps
+ * your application provide context-specific suggestions. Sometimes these suggestions are based
+ * on previous searches, or other actions taken by the user in an earlier session. A user may not
+ * wish for previous searches to be revealed to other users, for instance if they share their phone
+ * with a friend. If your application provides suggestions that can reveal previous activities,
+ * you should implement a "Clear History" menu, preference, or button. If you are using
+ * {@link android.provider.SearchRecentSuggestions}, you can simply call its
+ * {@link android.provider.SearchRecentSuggestions#clearHistory() clearHistory()} method from
+ * your "Clear History" UI. If you are implementing your own form of recent suggestions, you'll
+ * need to provide a similar a "clear history" API in your provider, and call it from your
+ * "Clear History" UI.
+ */
+public class SearchManager
+ implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener
+{
+ /**
+ * This is a shortcut definition for the default menu key to use for invoking search.
+ *
+ * See Menu.Item.setAlphabeticShortcut() for more information.
+ */
+ public final static char MENU_KEY = 's';
+
+ /**
+ * This is a shortcut definition for the default menu key to use for invoking search.
+ *
+ * See Menu.Item.setAlphabeticShortcut() for more information.
+ */
+ public final static int MENU_KEYCODE = KeyEvent.KEYCODE_S;
+
+ /**
+ * Intent extra data key: Use this key with
+ * {@link android.content.Intent#getStringExtra
+ * content.Intent.getStringExtra()}
+ * to obtain the query string from Intent.ACTION_SEARCH.
+ */
+ public final static String QUERY = "query";
+
+ /**
+ * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
+ * {@link android.content.Intent#getBundleExtra
+ * content.Intent.getBundleExtra()}
+ * to obtain any additional app-specific data that was inserted by the
+ * activity that launched the search.
+ */
+ public final static String APP_DATA = "app_data";
+
+ /**
+ * Intent app_data bundle key: Use this key with the bundle from
+ * {@link android.content.Intent#getBundleExtra
+ * content.Intent.getBundleExtra(APP_DATA)} to obtain the source identifier
+ * set by the activity that launched the search.
+ *
+ * @hide
+ */
+ public final static String SOURCE = "source";
+
+ /**
+ * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
+ * {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()}
+ * to obtain the keycode that the user used to trigger this query. It will be zero if the
+ * user simply pressed the "GO" button on the search UI. This is primarily used in conjunction
+ * with the keycode attribute in the actionkey element of your searchable.xml configuration
+ * file.
+ */
+ public final static String ACTION_KEY = "action_key";
+
+ /**
+ * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
+ * {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()}
+ * to obtain the action message that was defined for a particular search action key and/or
+ * suggestion. It will be null if the search was launched by typing "enter", touched the the
+ * "GO" button, or other means not involving any action key.
+ */
+ public final static String ACTION_MSG = "action_msg";
+
+ /**
+ * Uri path for queried suggestions data. This is the path that the search manager
+ * will use when querying your content provider for suggestions data based on user input
+ * (e.g. looking for partial matches).
+ * Typically you'll use this with a URI matcher.
+ */
+ public final static String SUGGEST_URI_PATH_QUERY = "search_suggest_query";
+
+ /**
+ * MIME type for suggestions data. You'll use this in your suggestions content provider
+ * in the getType() function.
+ */
+ public final static String SUGGEST_MIME_TYPE =
+ "vnd.android.cursor.dir/vnd.android.search.suggest";
+
+ /**
+ * Column name for suggestions cursor. <i>Unused - can be null or column can be omitted.</i>
+ */
+ public final static String SUGGEST_COLUMN_FORMAT = "suggest_format";
+ /**
+ * Column name for suggestions cursor. <i>Required.</i> This is the primary line of text that
+ * will be presented to the user as the suggestion.
+ */
+ public final static String SUGGEST_COLUMN_TEXT_1 = "suggest_text_1";
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column,
+ * then all suggestions will be provided in a two-line format. The second line of text is in
+ * a much smaller appearance.
+ */
+ public final static String SUGGEST_COLUMN_TEXT_2 = "suggest_text_2";
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column,
+ * then all suggestions will be provided in format that includes space for two small icons,
+ * one at the left and one at the right of each suggestion. The data in the column must
+ * be a a resource ID for the icon you wish to have displayed. If you include this column,
+ * you must also include {@link #SUGGEST_COLUMN_ICON_2}.
+ */
+ public final static String SUGGEST_COLUMN_ICON_1 = "suggest_icon_1";
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column,
+ * then all suggestions will be provided in format that includes space for two small icons,
+ * one at the left and one at the right of each suggestion. The data in the column must
+ * be a a resource ID for the icon you wish to have displayed. If you include this column,
+ * you must also include {@link #SUGGEST_COLUMN_ICON_1}.
+ */
+ public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2";
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i>
+ * this element exists at the given row, this is the action that will be used when
+ * forming the suggestion's intent. If the element is not provided, the action will be taken
+ * from the android:searchSuggestIntentAction field in your XML metadata. <i>At least one of
+ * these must be present for the suggestion to generate an intent.</i> Note: If your action is
+ * the same for all suggestions, it is more efficient to specify it using XML metadata and omit
+ * it from the cursor.
+ */
+ public final static String SUGGEST_COLUMN_INTENT_ACTION = "suggest_intent_action";
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i>
+ * this element exists at the given row, this is the data that will be used when
+ * forming the suggestion's intent. If the element is not provided, the data will be taken
+ * from the android:searchSuggestIntentData field in your XML metadata. If neither source
+ * is provided, the Intent's data field will be null. Note: If your data is
+ * the same for all suggestions, or can be described using a constant part and a specific ID,
+ * it is more efficient to specify it using XML metadata and omit it from the cursor.
+ */
+ public final static String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data";
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i>
+ * this element exists at the given row, then "/" and this value will be appended to the data
+ * field in the Intent. This should only be used if the data field has already been set to an
+ * appropriate base string.
+ */
+ public final static String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id";
+ /**
+ * Column name for suggestions cursor. <i>Required if action is
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</i> If this
+ * column exists <i>and</i> this element exists at the given row, this is the data that will be
+ * used when forming the suggestion's query.
+ */
+ public final static String SUGGEST_COLUMN_QUERY = "suggest_intent_query";
+
+
+ private final Context mContext;
+ private final Handler mHandler;
+
+ private SearchDialog mSearchDialog;
+
+ private OnDismissListener mDismissListener = null;
+ private OnCancelListener mCancelListener = null;
+
+ /*package*/ SearchManager(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ }
+ private static ISearchManager mService;
+
+ static {
+ mService = ISearchManager.Stub.asInterface(
+ ServiceManager.getService(Context.SEARCH_SERVICE));
+ }
+
+ /**
+ * Launch search UI.
+ *
+ * <p>The search manager will open a search widget in an overlapping
+ * window, and the underlying activity may be obscured. The search
+ * entry state will remain in effect until one of the following events:
+ * <ul>
+ * <li>The user completes the search. In most cases this will launch
+ * a search intent.</li>
+ * <li>The user uses the back, home, or other keys to exit the search.</li>
+ * <li>The application calls the {@link #stopSearch}
+ * method, which will hide the search window and return focus to the
+ * activity from which it was launched.</li>
+ *
+ * <p>Most applications will <i>not</i> use this interface to invoke search.
+ * The primary method for invoking search is to call
+ * {@link android.app.Activity#onSearchRequested Activity.onSearchRequested()} or
+ * {@link android.app.Activity#startSearch Activity.startSearch()}.
+ *
+ * @param initialQuery A search string can be pre-entered here, but this
+ * is typically null or empty.
+ * @param selectInitialQuery If true, the intial query will be preselected, which means that
+ * any further typing will replace it. This is useful for cases where an entire pre-formed
+ * query is being inserted. If false, the selection point will be placed at the end of the
+ * inserted query. This is useful when the inserted query is text that the user entered,
+ * and the user would expect to be able to keep typing. <i>This parameter is only meaningful
+ * if initialQuery is a non-empty string.</i>
+ * @param launchActivity The ComponentName of the activity that has launched this search.
+ * @param appSearchData An application can insert application-specific
+ * context here, in order to improve quality or specificity of its own
+ * searches. This data will be returned with SEARCH intent(s). Null if
+ * no extra data is required.
+ * @param globalSearch If false, this will only launch the search that has been specifically
+ * defined by the application (which is usually defined as a local search). If no default
+ * search is defined in the current application or activity, no search will be launched.
+ * If true, this will always launch a platform-global (e.g. web-based) search instead.
+ *
+ * @see android.app.Activity#onSearchRequested
+ * @see #stopSearch
+ */
+ public void startSearch(String initialQuery,
+ boolean selectInitialQuery,
+ ComponentName launchActivity,
+ Bundle appSearchData,
+ boolean globalSearch) {
+
+ if (mSearchDialog == null) {
+ mSearchDialog = new SearchDialog(mContext);
+ }
+
+ // activate the search manager and start it up!
+ mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
+ globalSearch);
+
+ mSearchDialog.setOnCancelListener(this);
+ mSearchDialog.setOnDismissListener(this);
+ }
+
+ /**
+ * Terminate search UI.
+ *
+ * <p>Typically the user will terminate the search UI by launching a
+ * search or by canceling. This function allows the underlying application
+ * or activity to cancel the search prematurely (for any reason).
+ *
+ * <p>This function can be safely called at any time (even if no search is active.)
+ *
+ * @see #startSearch
+ */
+ public void stopSearch() {
+ if (mSearchDialog != null) {
+ mSearchDialog.cancel();
+ }
+ }
+
+ /**
+ * Determine if the Search UI is currently displayed.
+ *
+ * This is provided primarily for application test purposes.
+ *
+ * @return Returns true if the search UI is currently displayed.
+ *
+ * @hide
+ */
+ public boolean isVisible() {
+ if (mSearchDialog != null) {
+ return mSearchDialog.isShowing();
+ }
+ return false;
+ }
+
+ /**
+ * See {@link #setOnDismissListener} for configuring your activity to monitor search UI state.
+ */
+ public interface OnDismissListener {
+ /**
+ * This method will be called when the search UI is dismissed. To make use if it, you must
+ * implement this method in your activity, and call {@link #setOnDismissListener} to
+ * register it.
+ */
+ public void onDismiss();
+ }
+
+ /**
+ * See {@link #setOnCancelListener} for configuring your activity to monitor search UI state.
+ */
+ public interface OnCancelListener {
+ /**
+ * This method will be called when the search UI is canceled. To make use if it, you must
+ * implement this method in your activity, and call {@link #setOnCancelListener} to
+ * register it.
+ */
+ public void onCancel();
+ }
+
+ /**
+ * Set or clear the callback that will be invoked whenever the search UI is dismissed.
+ *
+ * @param listener The {@link OnDismissListener} to use, or null.
+ */
+ public void setOnDismissListener(final OnDismissListener listener) {
+ mDismissListener = listener;
+ }
+
+ /**
+ * The callback from the search dialog when dismissed
+ * @hide
+ */
+ public void onDismiss(DialogInterface dialog) {
+ if (dialog == mSearchDialog) {
+ if (mDismissListener != null) {
+ mDismissListener.onDismiss();
+ }
+ }
+ }
+
+ /**
+ * Set or clear the callback that will be invoked whenever the search UI is canceled.
+ *
+ * @param listener The {@link OnCancelListener} to use, or null.
+ */
+ public void setOnCancelListener(final OnCancelListener listener) {
+ mCancelListener = listener;
+ }
+
+
+ /**
+ * The callback from the search dialog when canceled
+ * @hide
+ */
+ public void onCancel(DialogInterface dialog) {
+ if (dialog == mSearchDialog) {
+ if (mCancelListener != null) {
+ mCancelListener.onCancel();
+ }
+ }
+ }
+
+ /**
+ * Save instance state so we can recreate after a rotation.
+ *
+ * @hide
+ */
+ void saveSearchDialog(Bundle outState, String key) {
+ if (mSearchDialog != null && mSearchDialog.isShowing()) {
+ Bundle searchDialogState = mSearchDialog.onSaveInstanceState();
+ outState.putBundle(key, searchDialogState);
+ }
+ }
+
+ /**
+ * Restore instance state after a rotation.
+ *
+ * @hide
+ */
+ void restoreSearchDialog(Bundle inState, String key) {
+ Bundle searchDialogState = inState.getBundle(key);
+ if (searchDialogState != null) {
+ if (mSearchDialog == null) {
+ mSearchDialog = new SearchDialog(mContext);
+ }
+ mSearchDialog.onRestoreInstanceState(searchDialogState);
+ }
+ }
+
+ /**
+ * Hook for updating layout on a rotation
+ *
+ * @hide
+ */
+ void onConfigurationChanged(Configuration newConfig) {
+ if (mSearchDialog != null && mSearchDialog.isShowing()) {
+ mSearchDialog.onConfigurationChanged(newConfig);
+ }
+ }
+
+}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
new file mode 100644
index 0000000..a6a436f
--- /dev/null
+++ b/core/java/android/app/Service.java
@@ -0,0 +1,378 @@
+/*
+ * 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.app;
+
+import android.content.ComponentCallbacks;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ContextWrapper;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+import android.os.IBinder;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A Service is an application component that runs in the background, not
+ * interacting with the user, for an indefinite period of time. Each service
+ * class must have a corresponding
+ * {@link android.R.styleable#AndroidManifestService &lt;service&gt;}
+ * declaration in its package's <code>AndroidManifest.xml</code>. Services
+ * can be started with
+ * {@link android.content.Context#startService Context.startService()} and
+ * {@link android.content.Context#bindService Context.bindService()}.
+ *
+ * <p>Note that services, like other application objects, run in the main
+ * thread of their hosting process. This means that, if your service is going
+ * to do any CPU intensive (such as MP3 playback) or blocking (such as
+ * networking) operations, it should spawn its own thread in which to do that
+ * work. More information on this can be found in
+ * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
+ * Processes and Threads</a>.</p>
+ *
+ * <p>The Service class is an important part of an
+ * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">application's overall lifecycle</a>.</p>
+ *
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#ServiceLifecycle">Service Lifecycle</a>
+ * <li><a href="#Permissions">Permissions</a>
+ * <li><a href="#ProcessLifecycle">Process Lifecycle</a>
+ * </ol>
+ *
+ * <a name="ServiceLifecycle"></a>
+ * <h3>Service Lifecycle</h3>
+ *
+ * <p>There are two reasons that a service can be run by the system. If someone
+ * calls {@link android.content.Context#startService Context.startService()} then the system will
+ * retrieve the service (creating it and calling its {@link #onCreate} method
+ * if needed) and then call its {@link #onStart} method with the
+ * arguments supplied by the client. The service will at this point continue
+ * running until {@link android.content.Context#stopService Context.stopService()} or
+ * {@link #stopSelf()} is called. Note that multiple calls to
+ * Context.startService() do not nest (though they do result in multiple corresponding
+ * calls to onStart()), so no matter how many times it is started a service
+ * will be stopped once Context.stopService() or stopSelf() is called.
+ *
+ * <p>Clients can also use {@link android.content.Context#bindService Context.bindService()} to
+ * obtain a persistent connection to a service. This likewise creates the
+ * service if it is not already running (calling {@link #onCreate} while
+ * doing so), but does not call onStart(). The client will receive the
+ * {@link android.os.IBinder} object that the service returns from its
+ * {@link #onBind} method, allowing the client to then make calls back
+ * to the service. The service will remain running as long as the connection
+ * is established (whether or not the client retains a reference on the
+ * service's IBinder). Usually the IBinder returned is for a complex
+ * interface that has been <a href="{@docRoot}guide/developing/tools/aidl.html">written
+ * in aidl</a>.
+ *
+ * <p>A service can be both started and have connections bound to it. In such
+ * a case, the system will keep the service running as long as either it is
+ * started <em>or</em> there are one or more connections to it with the
+ * {@link android.content.Context#BIND_AUTO_CREATE Context.BIND_AUTO_CREATE}
+ * flag. Once neither
+ * of these situations hold, the service's {@link #onDestroy} method is called
+ * and the service is effectively terminated. All cleanup (stopping threads,
+ * unregistering receivers) should be complete upon returning from onDestroy().
+ *
+ * <a name="Permissions"></a>
+ * <h3>Permissions</h3>
+ *
+ * <p>Global access to a service can be enforced when it is declared in its
+ * manifest's {@link android.R.styleable#AndroidManifestService &lt;service&gt;}
+ * tag. By doing so, other applications will need to declare a corresponding
+ * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
+ * element in their own manifest to be able to start, stop, or bind to
+ * the service.
+ *
+ * <p>In addition, a service can protect individual IPC calls into it with
+ * permissions, by calling the
+ * {@link #checkCallingPermission}
+ * method before executing the implementation of that call.
+ *
+ * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
+ * document for more information on permissions and security in general.
+ *
+ * <a name="ProcessLifecycle"></a>
+ * <h3>Process Lifecycle</h3>
+ *
+ * <p>The Android system will attempt to keep the process hosting a service
+ * around as long as the service has been started or has clients bound to it.
+ * When running low on memory and needing to kill existing processes, the
+ * priority of a process hosting the service will be the higher of the
+ * following possibilities:
+ *
+ * <ul>
+ * <li><p>If the service is currently executing code in its
+ * {@link #onCreate onCreate()}, {@link #onStart onStart()},
+ * or {@link #onDestroy onDestroy()} methods, then the hosting process will
+ * be a foreground process to ensure this code can execute without
+ * being killed.
+ * <li><p>If the service has been started, then its hosting process is considered
+ * to be less important than any processes that are currently visible to the
+ * user on-screen, but more important than any process not visible. Because
+ * only a few processes are generally visible to the user, this means that
+ * the service should not be killed except in extreme low memory conditions.
+ * <li><p>If there are clients bound to the service, then the service's hosting
+ * process is never less important than the most important client. That is,
+ * if one of its clients is visible to the user, then the service itself is
+ * considered to be visible.
+ * </ul>
+ *
+ * <p>Note this means that most of the time your service is running, it may
+ * be killed by the system if it is under heavy memory pressure. If this
+ * happens, the system will later try to restart the service. An important
+ * consequence of this is that if you implement {@link #onStart onStart()}
+ * to schedule work to be done asynchronously or in another thread, then you
+ * may want to write information about that work into persistent storage
+ * during the onStart() call so that it does not get lost if the service later
+ * gets killed.
+ *
+ * <p>Other application components running in the same process as the service
+ * (such as an {@link android.app.Activity}) can, of course, increase the
+ * importance of the overall
+ * process beyond just the importance of the service itself.
+ */
+public abstract class Service extends ContextWrapper implements ComponentCallbacks {
+ private static final String TAG = "Service";
+
+ public Service() {
+ super(null);
+ }
+
+ /** Return the application that owns this service. */
+ public final Application getApplication() {
+ return mApplication;
+ }
+
+ /**
+ * Called by the system when the service is first created. Do not call this method directly.
+ */
+ public void onCreate() {
+ }
+
+ /**
+ * Called by the system every time a client explicitly starts the service by calling
+ * {@link android.content.Context#startService}, providing the arguments it supplied and a
+ * unique integer token representing the start request. Do not call this method directly.
+ *
+ * @param intent The Intent supplied to {@link android.content.Context#startService},
+ * as given.
+ * @param startId A unique integer representing this specific request to
+ * start. Use with {@link #stopSelfResult(int)}.
+ *
+ * @see #stopSelfResult(int)
+ */
+ public void onStart(Intent intent, int startId) {
+ }
+
+ /**
+ * Called by the system to notify a Service that it is no longer used and is being removed. The
+ * service should clean up an resources it holds (threads, registered
+ * receivers, etc) at this point. Upon return, there will be no more calls
+ * in to this Service object and it is effectively dead. Do not call this method directly.
+ */
+ public void onDestroy() {
+ }
+
+ public void onConfigurationChanged(Configuration newConfig) {
+ }
+
+ public void onLowMemory() {
+ }
+
+ /**
+ * Return the communication channel to the service. May return null if
+ * clients can not bind to the service. The returned
+ * {@link android.os.IBinder} is usually for a complex interface
+ * that has been <a href="{@docRoot}guide/developing/tools/aidl.html">described using
+ * aidl</a>.
+ *
+ * <p><em>Note that unlike other application components, calls on to the
+ * IBinder interface returned here may not happen on the main thread
+ * of the process</em>. More information about this can be found
+ * in <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
+ * Processes and Threads</a>.</p>
+ *
+ * @param intent The Intent that was used to bind to this service,
+ * as given to {@link android.content.Context#bindService
+ * Context.bindService}. Note that any extras that were included with
+ * the Intent at that point will <em>not</em> be seen here.
+ *
+ * @return Return an IBinder through which clients can call on to the
+ * service.
+ */
+ public abstract IBinder onBind(Intent intent);
+
+ /**
+ * Called when all clients have disconnected from a particular interface
+ * published by the service. The default implementation does nothing and
+ * returns false.
+ *
+ * @param intent The Intent that was used to bind to this service,
+ * as given to {@link android.content.Context#bindService
+ * Context.bindService}. Note that any extras that were included with
+ * the Intent at that point will <em>not</em> be seen here.
+ *
+ * @return Return true if you would like to have the service's
+ * {@link #onRebind} method later called when new clients bind to it.
+ */
+ public boolean onUnbind(Intent intent) {
+ return false;
+ }
+
+ /**
+ * Called when new clients have connected to the service, after it had
+ * previously been notified that all had disconnected in its
+ * {@link #onUnbind}. This will only be called if the implementation
+ * of {@link #onUnbind} was overridden to return true.
+ *
+ * @param intent The Intent that was used to bind to this service,
+ * as given to {@link android.content.Context#bindService
+ * Context.bindService}. Note that any extras that were included with
+ * the Intent at that point will <em>not</em> be seen here.
+ */
+ public void onRebind(Intent intent) {
+ }
+
+ /**
+ * Stop the service, if it was previously started. This is the same as
+ * calling {@link android.content.Context#stopService} for this particular service.
+ *
+ * @see #stopSelfResult(int)
+ */
+ public final void stopSelf() {
+ stopSelf(-1);
+ }
+
+ /**
+ * Old version of {@link #stopSelfResult} that doesn't return a result.
+ *
+ * @see #stopSelfResult
+ */
+ public final void stopSelf(int startId) {
+ if (mActivityManager == null) {
+ return;
+ }
+ try {
+ mActivityManager.stopServiceToken(
+ new ComponentName(this, mClassName), mToken, startId);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ /**
+ * Stop the service, if the most recent time it was started was
+ * <var>startId</var>. This is the same as calling {@link
+ * android.content.Context#stopService} for this particular service but allows you to
+ * safely avoid stopping if there is a start request from a client that you
+ * haven't yet see in {@link #onStart}.
+ *
+ * @param startId The most recent start identifier received in {@link
+ * #onStart}.
+ * @return Returns true if the startId matches the last start request
+ * and the service will be stopped, else false.
+ *
+ * @see #stopSelf()
+ */
+ public final boolean stopSelfResult(int startId) {
+ if (mActivityManager == null) {
+ return false;
+ }
+ try {
+ return mActivityManager.stopServiceToken(
+ new ComponentName(this, mClassName), mToken, startId);
+ } catch (RemoteException ex) {
+ }
+ return false;
+ }
+
+ /**
+ * Control whether this service is considered to be a foreground service.
+ * By default services are background, meaning that if the system needs to
+ * kill them to reclaim more memory (such as to display a large page in a
+ * web browser), they can be killed without too much harm. You can set this
+ * flag if killing your service would be disruptive to the user: such as
+ * if your service is performing background music playback, so the user
+ * would notice if their music stopped playing.
+ *
+ * @param isForeground Determines whether this service is considered to
+ * be foreground (true) or background (false).
+ */
+ public final void setForeground(boolean isForeground) {
+ if (mActivityManager == null) {
+ return;
+ }
+ try {
+ mActivityManager.setServiceForeground(
+ new ComponentName(this, mClassName), mToken, isForeground);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ /**
+ * Print the Service's state into the given stream. This gets invoked if
+ * you run "adb shell dumpsys activity service <yourservicename>".
+ * This is distinct from "dumpsys <servicename>", which only works for
+ * named system services and which invokes the {@link IBinder#dump} method
+ * on the {@link IBinder} interface registered with ServiceManager.
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param writer The PrintWriter 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 writer, String[] args) {
+ writer.println("nothing to dump");
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ //Log.i("Service", "Finalizing Service: " + this);
+ }
+
+ // ------------------ Internal API ------------------
+
+ /**
+ * @hide
+ */
+ public final void attach(
+ Context context,
+ ActivityThread thread, String className, IBinder token,
+ Application application, Object activityManager) {
+ attachBaseContext(context);
+ mThread = thread; // NOTE: unused - remove?
+ mClassName = className;
+ mToken = token;
+ mApplication = application;
+ mActivityManager = (IActivityManager)activityManager;
+ }
+
+ final String getClassName() {
+ return mClassName;
+ }
+
+ // set by the thread after the constructor and before onCreate(Bundle icicle) is called.
+ private ActivityThread mThread = null;
+ private String mClassName = null;
+ private IBinder mToken = null;
+ private Application mApplication = null;
+ private IActivityManager mActivityManager = null;
+}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
new file mode 100644
index 0000000..51d7393
--- /dev/null
+++ b/core/java/android/app/StatusBarManager.java
@@ -0,0 +1,139 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Allows an app to control the status bar.
+ *
+ * @hide
+ */
+public class StatusBarManager {
+ /**
+ * Flag for {@link #disable} to make the status bar not expandable. Unless you also
+ * set {@link #DISABLE_NOTIFICATIONS}, new notifications will continue to show.
+ */
+ public static final int DISABLE_EXPAND = 0x00000001;
+
+ /**
+ * Flag for {@link #disable} to hide notification icons and ticker text.
+ */
+ public static final int DISABLE_NOTIFICATION_ICONS = 0x00000002;
+
+ /**
+ * Flag for {@link #disable} to disable incoming notification alerts. This will not block
+ * icons, but it will block sound, vibrating and other visual or aural notifications.
+ */
+ public static final int DISABLE_NOTIFICATION_ALERTS = 0x00000004;
+
+ /**
+ * Re-enable all of the status bar features that you've disabled.
+ */
+ public static final int DISABLE_NONE = 0x00000000;
+
+ private Context mContext;
+ private IStatusBar mService;
+ private IBinder mToken = new Binder();
+
+ StatusBarManager(Context context) {
+ mContext = context;
+ mService = IStatusBar.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ /**
+ * Disable some features in the status bar. Pass the bitwise-or of the DISABLE_* flags.
+ * To re-enable everything, pass {@link #DISABLE_NONE}.
+ */
+ public void disable(int what) {
+ try {
+ mService.disable(what, mToken, mContext.getPackageName());
+ } catch (RemoteException ex) {
+ // system process is dead anyway.
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Expand the status bar.
+ */
+ public void expand() {
+ try {
+ mService.activate();
+ } catch (RemoteException ex) {
+ // system process is dead anyway.
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Collapse the status bar.
+ */
+ public void collapse() {
+ try {
+ mService.deactivate();
+ } catch (RemoteException ex) {
+ // system process is dead anyway.
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Toggle the status bar.
+ */
+ public void toggle() {
+ try {
+ mService.toggle();
+ } catch (RemoteException ex) {
+ // system process is dead anyway.
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public IBinder addIcon(String slot, int iconId, int iconLevel) {
+ try {
+ return mService.addIcon(slot, mContext.getPackageName(), iconId, iconLevel);
+ } catch (RemoteException ex) {
+ // system process is dead anyway.
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public void updateIcon(IBinder key, String slot, int iconId, int iconLevel) {
+ try {
+ mService.updateIcon(key, slot, mContext.getPackageName(), iconId, iconLevel);
+ } catch (RemoteException ex) {
+ // system process is dead anyway.
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public void removeIcon(IBinder key) {
+ try {
+ mService.removeIcon(key);
+ } catch (RemoteException ex) {
+ // system process is dead anyway.
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/core/java/android/app/TabActivity.java b/core/java/android/app/TabActivity.java
new file mode 100644
index 0000000..033fa0c
--- /dev/null
+++ b/core/java/android/app/TabActivity.java
@@ -0,0 +1,148 @@
+/*
+ * 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.app;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TabHost;
+import android.widget.TabWidget;
+import android.widget.TextView;
+
+/**
+ * An activity that contains and runs multiple embedded activities or views.
+ */
+public class TabActivity extends ActivityGroup {
+ private TabHost mTabHost;
+ private String mDefaultTab = null;
+ private int mDefaultTabIndex = -1;
+
+ public TabActivity() {
+ }
+
+ /**
+ * Sets the default tab that is the first tab highlighted.
+ *
+ * @param tag the name of the default tab
+ */
+ public void setDefaultTab(String tag) {
+ mDefaultTab = tag;
+ mDefaultTabIndex = -1;
+ }
+
+ /**
+ * Sets the default tab that is the first tab highlighted.
+ *
+ * @param index the index of the default tab
+ */
+ public void setDefaultTab(int index) {
+ mDefaultTab = null;
+ mDefaultTabIndex = index;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ ensureTabHost();
+ String cur = state.getString("currentTab");
+ if (cur != null) {
+ mTabHost.setCurrentTabByTag(cur);
+ }
+ if (mTabHost.getCurrentTab() < 0) {
+ if (mDefaultTab != null) {
+ mTabHost.setCurrentTabByTag(mDefaultTab);
+ } else if (mDefaultTabIndex >= 0) {
+ mTabHost.setCurrentTab(mDefaultTabIndex);
+ }
+ }
+ }
+
+ @Override
+ protected void onPostCreate(Bundle icicle) {
+ super.onPostCreate(icicle);
+
+ ensureTabHost();
+
+ if (mTabHost.getCurrentTab() == -1) {
+ mTabHost.setCurrentTab(0);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ String currentTabTag = mTabHost.getCurrentTabTag();
+ if (currentTabTag != null) {
+ outState.putString("currentTab", currentTabTag);
+ }
+ }
+
+ /**
+ * Updates the screen state (current list and other views) when the
+ * content changes.
+ *
+ *@see Activity#onContentChanged()
+ */
+ @Override
+ public void onContentChanged() {
+ super.onContentChanged();
+ mTabHost = (TabHost) findViewById(com.android.internal.R.id.tabhost);
+
+ if (mTabHost == null) {
+ throw new RuntimeException(
+ "Your content must have a TabHost whose id attribute is " +
+ "'android.R.id.tabhost'");
+ }
+ mTabHost.setup(getLocalActivityManager());
+ }
+
+ private void ensureTabHost() {
+ if (mTabHost == null) {
+ this.setContentView(com.android.internal.R.layout.tab_content);
+ }
+ }
+
+ @Override
+ protected void
+ onChildTitleChanged(Activity childActivity, CharSequence title) {
+ // Dorky implementation until we can have multiple activities running.
+ if (getLocalActivityManager().getCurrentActivity() == childActivity) {
+ View tabView = mTabHost.getCurrentTabView();
+ if (tabView != null && tabView instanceof TextView) {
+ ((TextView) tabView).setText(title);
+ }
+ }
+ }
+
+ /**
+ * Returns the {@link TabHost} the activity is using to host its tabs.
+ *
+ * @return the {@link TabHost} the activity is using to host its tabs.
+ */
+ public TabHost getTabHost() {
+ ensureTabHost();
+ return mTabHost;
+ }
+
+ /**
+ * Returns the {@link TabWidget} the activity is using to draw the actual tabs.
+ *
+ * @return the {@link TabWidget} the activity is using to draw the actual tabs.
+ */
+ public TabWidget getTabWidget() {
+ return mTabHost.getTabWidget();
+ }
+}
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
new file mode 100644
index 0000000..002b01f
--- /dev/null
+++ b/core/java/android/app/TimePickerDialog.java
@@ -0,0 +1,162 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+import android.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TimePicker;
+import android.widget.TimePicker.OnTimeChangedListener;
+
+import com.android.internal.R;
+
+import java.util.Calendar;
+
+/**
+ * A dialog that prompts the user for the time of day using a {@link TimePicker}.
+ */
+public class TimePickerDialog extends AlertDialog implements OnClickListener,
+ OnTimeChangedListener {
+
+ /**
+ * The callback interface used to indicate the user is done filling in
+ * the time (they clicked on the 'Set' button).
+ */
+ public interface OnTimeSetListener {
+
+ /**
+ * @param view The view associated with this listener.
+ * @param hourOfDay The hour that was set.
+ * @param minute The minute that was set.
+ */
+ void onTimeSet(TimePicker view, int hourOfDay, int minute);
+ }
+
+ private static final String HOUR = "hour";
+ private static final String MINUTE = "minute";
+ private static final String IS_24_HOUR = "is24hour";
+
+ private final TimePicker mTimePicker;
+ private final OnTimeSetListener mCallback;
+ private final Calendar mCalendar;
+ private final java.text.DateFormat mDateFormat;
+
+ int mInitialHourOfDay;
+ int mInitialMinute;
+ boolean mIs24HourView;
+
+ /**
+ * @param context Parent.
+ * @param callBack How parent is notified.
+ * @param hourOfDay The initial hour.
+ * @param minute The initial minute.
+ * @param is24HourView Whether this is a 24 hour view, or AM/PM.
+ */
+ public TimePickerDialog(Context context,
+ OnTimeSetListener callBack,
+ int hourOfDay, int minute, boolean is24HourView) {
+ this(context, com.android.internal.R.style.Theme_Dialog_Alert,
+ callBack, hourOfDay, minute, is24HourView);
+ }
+
+ /**
+ * @param context Parent.
+ * @param theme the theme to apply to this dialog
+ * @param callBack How parent is notified.
+ * @param hourOfDay The initial hour.
+ * @param minute The initial minute.
+ * @param is24HourView Whether this is a 24 hour view, or AM/PM.
+ */
+ public TimePickerDialog(Context context,
+ int theme,
+ OnTimeSetListener callBack,
+ int hourOfDay, int minute, boolean is24HourView) {
+ super(context, theme);
+ mCallback = callBack;
+ mInitialHourOfDay = hourOfDay;
+ mInitialMinute = minute;
+ mIs24HourView = is24HourView;
+
+ mDateFormat = DateFormat.getTimeFormat(context);
+ mCalendar = Calendar.getInstance();
+ updateTitle(mInitialHourOfDay, mInitialMinute);
+
+ setButton(context.getText(R.string.date_time_set), this);
+ setButton2(context.getText(R.string.cancel), (OnClickListener) null);
+ setIcon(R.drawable.ic_dialog_time);
+
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(R.layout.time_picker_dialog, null);
+ setView(view);
+ mTimePicker = (TimePicker) view.findViewById(R.id.timePicker);
+
+ // initialize state
+ mTimePicker.setCurrentHour(mInitialHourOfDay);
+ mTimePicker.setCurrentMinute(mInitialMinute);
+ mTimePicker.setIs24HourView(mIs24HourView);
+ mTimePicker.setOnTimeChangedListener(this);
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (mCallback != null) {
+ mTimePicker.clearFocus();
+ mCallback.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
+ mTimePicker.getCurrentMinute());
+ }
+ }
+
+ public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
+ updateTitle(hourOfDay, minute);
+ }
+
+ public void updateTime(int hourOfDay, int minutOfHour) {
+ mTimePicker.setCurrentHour(hourOfDay);
+ mTimePicker.setCurrentMinute(minutOfHour);
+ }
+
+ private void updateTitle(int hour, int minute) {
+ mCalendar.set(Calendar.HOUR_OF_DAY, hour);
+ mCalendar.set(Calendar.MINUTE, minute);
+ setTitle(mDateFormat.format(mCalendar.getTime()));
+ }
+
+ @Override
+ public Bundle onSaveInstanceState() {
+ Bundle state = super.onSaveInstanceState();
+ state.putInt(HOUR, mTimePicker.getCurrentHour());
+ state.putInt(MINUTE, mTimePicker.getCurrentMinute());
+ state.putBoolean(IS_24_HOUR, mTimePicker.is24HourView());
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ int hour = savedInstanceState.getInt(HOUR);
+ int minute = savedInstanceState.getInt(MINUTE);
+ mTimePicker.setCurrentHour(hour);
+ mTimePicker.setCurrentMinute(minute);
+ mTimePicker.setIs24HourView(savedInstanceState.getBoolean(IS_24_HOUR));
+ mTimePicker.setOnTimeChangedListener(this);
+ updateTitle(hour, minute);
+ }
+}
diff --git a/core/java/android/app/package.html b/core/java/android/app/package.html
new file mode 100644
index 0000000..048ee93
--- /dev/null
+++ b/core/java/android/app/package.html
@@ -0,0 +1,72 @@
+<html>
+<head>
+<script type="text/javascript" src="http://www.corp.google.com/style/prettify.js"></script>
+<script src="http://www.corp.google.com/eng/techpubs/include/navbar.js" type="text/javascript"></script>
+</head>
+
+<body>
+
+<p>High-level classes encapsulating the overall Android application model.
+The central class is {@link android.app.Activity}, with other top-level
+application components being defined by {@link android.app.Service} and,
+from the {@link android.content} package, {@link android.content.BroadcastReceiver}
+and {@link android.content.ContentProvider}. It also includes application
+tools, such as dialogs and notifications.</p>
+
+<p>This package builds on top of the lower-level Android packages
+{@link android.widget}, {@link android.view}, {@link android.content},
+{@link android.text}, {@link android.graphics}, {@link android.os}, and
+{@link android.util}.</p>
+
+<p>An {@link android.app.Activity Activity} is a specific operation the
+user can perform, generally corresponding
+to one screen in the user interface.
+It is the basic building block of an Android application.
+Examples of activities are "view the
+list of people," "view the details of a person," "edit information about
+a person," "view an image," etc. Switching from one activity to another
+generally implies adding a new entry on the navigation history; that is,
+going "back" means moving to the previous activity you were doing.</p>
+
+<p>A set of related activities can be grouped together as a "task". Until
+a new task is explicitly specified, all activites you start are considered
+to be part of the current task. While the only way to navigate between
+individual activities is by going "back" in the history stack, the group
+of activities in a task can be moved in relation to other tasks: for example
+to the front or the back of the history stack. This mechanism can be used
+to present to the user a list of things they have been doing, moving
+between them without disrupting previous work.
+</p>
+
+<p>A complete "application" is a set of activities that allow the user to do a
+cohesive group of operations -- such as working with contacts, working with a
+calendar, messaging, etc. Though there can be a custom application object
+associated with a set of activities, in many cases this is not needed --
+each activity provides a particular path into one of the various kinds of
+functionality inside of the application, serving as its on self-contained
+"mini application".
+</p>
+
+<p>This approach allows an application to be broken into pieces, which
+can be reused and replaced in a variety of ways. Consider, for example,
+a "camera application." There are a number of things this application
+must do, each of which is provided by a separate activity: take a picture
+(creating a new image), browse through the existing images, display a
+specific image, etc. If the "contacts application" then wants to let the
+user associate an image with a person, it can simply launch the existing
+"take a picture" or "select an image" activity that is part of the camera
+application and attach the picture it gets back.
+</p>
+
+<p>Note that there is no hard relationship between tasks the user sees and
+applications the developer writes. A task can be composed of activities from
+multiple applications (such as the contact application using an activity in
+the camera application to get a picture for a person), and multiple active
+tasks may be running for the same application (such as editing e-mail messages
+to two different people). The way tasks are organized is purely a UI policy
+decided by the system; for example, typically a new task is started when the
+user goes to the application launcher and selects an application.
+</p>
+
+</body>
+</html>