Microsoft V Motorola ITC '376 Patent Claim Chart

Download as pdf
Download as pdf
You are on page 1of 265
At a glance
Powered by AI
The document discusses the Android operating system and how applications on the Motorola Droid 2 can register to receive notifications about changes to different state properties using the Android API.

The Android Application Program Interface (API) allows applications to register to receive intent broadcasts using the registerReceiver API.

Applications can register to receive intent broadcasts for different state properties including those related to the device's battery level, network connectivity, and other properties discussed in the 'Summary' table provided in the Android documentation.

EXHIBIT 56

376 Patent 10. A system for state management and notifications, comprising:

Exemplary Motorola Devices To the extent the preamble is construed to be limiting, the Motorola Droid 2 includes a system for state management and notifications. The Droid 2 includes the Android operating system platform (Android). (See, Ex. A, Droid 2 by Motorola Overview, http://www.motorola.com/Consumers/US-EN/Consumer-Productand-Services/Mobile-Phones/Motorola-DROID-2-US-EN) (visited Sept. 9, 2010.) An exemplary accused device the Droid 2 is depicted in Figure 10-1 below.

a data store on a mobile device that is arranged to store information relating to state properties, wherein at least some of the state properties are modified by different components;

Figure 10-1. The Droid 2 includes a data store that is arranged to store information relating to state properties, wherein at least some of the state properties are modified by different components. By way of example, the Droid 2s state properties are stored in the systems memory; e.g., its RAM memory. The state properties are modified by different components in the Droid 2. By way of example, some state properties related to the device's battery are modified by the update() function of BatteryService.java and/or state properties related to the device's network connectivity are modified by the setDetailedState() function of NetworkInfo.java. (See Ex. B; Ex. C.) The Droid 2 includes an Application Program Interface (API) configured to perform operations relating to the state properties. By way of example, the registerReceiver API allows applications to register to receive intent broadcasts. (See Ex. D.) 1

an Application Program Interface (API) configured to perform operations relating to the state

376 Patent properties;

Exemplary Motorola Devices Numerous possible state properties for intent broadcasts are set forth in the Summary table located at Ex. E, http://developer.android.com/reference/android/content/Intent.htm l. The Droid 2 includes client applications that are configured to automatically register notification requests and receive notifications in response to a change in a state property of the mobile device for which they have registered. By way of example, client applications register for intent broadcasts using the registerReceiver API. The Droid 2 includes client applications, wherein the notification requests indicate when the clients should receive notifications in response to changes associated with the state properties.

client applications on the mobile device that are configured to automatically register notification requests and receive notifications in response to a change in a state property of the mobile device for which they have registered,

wherein the notification requests indicate when the By way of example, client applications can register for intent clients should receive broadcasts such as those discussed above. (See Ex. E.) notifications in response to changes associated with the Also, the Droid 2 includes client applications, wherein execution state properties, and of the client applications is dependant upon a received notification. wherein execution of the By way of example, the execution of the a client Phone application client applications is dependent upon a received depends on its receipt of intent broadcasts, such as through its invocation of sendBatteryLow() . (See Ex. F, CallNotifier.java.) notification; As another example, the execution of a web browser application depends on its receipt of intent broadcasts, such as through its invocation of onNetworkToggle(). (See Ex. G, BrowserActivity.java.) wherein the change in the The change in the state property is responsive to an event that state property is responsive originates on the Droid 2. to an event that originates on the mobile device; By way of example, broadcast intents correspond to system events related to the devices battery, its network connectivity, whether it is being charged, whether the screen has turned on or off, whether it has been reboot, and the like. (See Summary table set forth at Ex. E.) a notification list stored The Droid 2 includes a notification list stored within the data store within the data store that is that is arranged to store the clients that have been registered to arranged to store the clients receive notification requests. that have been registered to receive notification By way of example, when an application registers for an intent requests; broadcast using registerReceiver, the Droid 2 adds that application to the mTypedActionToFilter of the class HashMap. (See Ex. H, HashMap.java.) 2

376 Patent a notification broker on the mobile device that is coupled to the data store, the notification list, and the clients, wherein the notification broker, includes functionality configured to perform the following actions, including to: receive a notification request to add at least one client to the notification list;

Exemplary Motorola Devices The Droid 2 includes a notification broker that is coupled to the data store, the notification list, and the clients. By way of example, the Droid 2 includes an Intent Resolver class that generates, adds applications to, and accesses the mTypedActionToFilter table. (See Ex. I, IntentResolver.java)

The Droid 2 can receive a notification request to add at least one client to the notification.

By way of example, when a client application registers for an intent using registerReciever, Intent Resolver receives a notification request to add a client to the notification list. (See Ex. I.) add the at least one client to The Droid 2 can add at least one client to the notification list. the notification list; and By way of example, after the Intent Resolver receives a notification request to add a client to the notification list, it allows for adding a client to the notification list. (See Ex. I.) determine when a The Droid 2 can determine when a registered state property registered state property changes, and when the state property changes, determine the changes, and when the state clients to receive a notification, and notify the determined clients property changes, of the state property change. determine the clients to receive a notification, and By way of example, when a state property changes, the Intent notify the determined Resolver determines the clients to receive the notification and clients of the state property notifies the determined clients of the state property change. (See change. Ex. I; Ex. J, ContextImpl.java.)

ATTACHMENT A

MOTOROLA FOR BUSINESS

SIGN UP FOR EMAIL UPDATES

USA (Change Location)

Search

Overview

Features

Tech Specs

Owner Support

|
Like

Share
2,305 people like this.

Multimedia Station for DROID 2 by Motorola


For music, movies and more, this is the accessory you'll want to get. CHECK IT OUT

Car Mount for DROID 2 by Motorola


This dock triggers an app that allows access to music, Google Maps Navigation and more. CHECK IT OUT

Owner Support
Learn how to get help for your Motorola product. FIND IT

Motorola Home > Consumer Product & Services > Mobile Phones > DROID 2 by Motorola

The Office, property of NBC Studios used under license. PrimeTime2Go is a trademark of Quickplay Inc. Google, Google Search, Google Maps, Gmail, Google Latitude, Google Talk, YouTube, Google Quick Search Box, Android, and Android Market are trademarks of Google, Inc. DROID is a trademark of Lucasfilm Ltd. and its related companies. Used under license. Airtime, data charges, and/or additional charges may apply to applications. Certain applications or features may not be available in all areas. Some listed applications may be trial versions. See your service provider for details.

ABOUT MOTOROLA CAREERS DEVELOPERS INVESTORS MEDIA CENTER PRIVACY POLICY TERMS OF USE CONTACT US

SITE FEEDBACK
2010 MOTOROLA,INC.ALL RIGHTS RESERVED

SITE MAP

ATTACHMENT B

/* * 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 com.android.server; import com.android.internal.app.IBatteryStats; import com.android.server.am.BatteryStatsService; import import import import import import import import import import import import import import import import import import import import import import android.app.ActivityManagerNative; android.content.ContentResolver; android.content.Context; android.content.Intent; android.content.pm.PackageManager; android.os.BatteryManager; android.os.Binder; android.os.IBinder; android.os.DropBoxManager; android.os.RemoteException; android.os.ServiceManager; android.os.SystemClock; android.os.UEventObserver; android.provider.Settings; android.util.EventLog; android.util.Slog; java.io.File; java.io.FileDescriptor; java.io.FileInputStream; java.io.FileOutputStream; java.io.IOException; java.io.PrintWriter;

/** * <p>BatteryService monitors the charging status, and charge level of the device * battery. When these values change this service broadcasts the new values * to all {@link android.content.BroadcastReceiver IntentReceivers} that are * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED * BATTERY_CHANGED} action.</p> * <p>The new values are stored in the Intent data and can be retrieved by * calling {@link android.content.Intent#getExtra Intent.getExtra} with the * following keys:</p> * <p>&quot;scale&quot; - int, the maximum value for the charge level</p> * <p>&quot;level&quot; - int, charge level, from 0 through &quot;scale&quot; inclusive</p> * <p>&quot;status&quot; - String, the current charging status.<br /> * <p>&quot;health&quot; - String, the current battery health.<br /> * <p>&quot;present&quot; - boolean, true if the battery is present<br /> * <p>&quot;icon-small&quot; - int, suggested small icon to use for this state</p> * <p>&quot;plugged&quot; - int, 0 if the device is not plugged in; 1 if plugged * into an AC power adapter; 2 if plugged in via USB.</p> * <p>&quot;voltage&quot; - int, current battery voltage in millivolts</p> * <p>&quot;temperature&quot; - int, current battery temperature in tenths of * a degree Centigrade</p> * <p>&quot;technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p> */ class BatteryService extends Binder { private static final String TAG = BatteryService.class.getSimpleName(); private static final boolean LOCAL_LOGV = false; static final int BATTERY_SCALE = 100; // battery capacity is a percentage

// Used locally for determining when to make a last ditch effort to log // discharge stats before the device dies. private static final int CRITICAL_BATTERY_LEVEL = 4; private static final int DUMP_MAX_LENGTH = 24 * 1024;

private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "-u" }; private static final String BATTERY_STATS_SERVICE_NAME = "batteryinfo"; private static final String DUMPSYS_DATA_PATH = "/data/system/"; // This should probably be exposed in the API, though it's not critical private static final int BATTERY_PLUGGED_NONE = 0; private final Context mContext; private final IBatteryStats mBatteryStats; private private private private private private private private private private private private private private private private private boolean mAcOnline; boolean mUsbOnline; int mBatteryStatus; int mBatteryHealth; boolean mBatteryPresent; int mBatteryLevel; int mBatteryVoltage; int mBatteryTemperature; String mBatteryTechnology; boolean mBatteryLevelCritical; int mLastBatteryStatus; int mLastBatteryHealth; boolean mLastBatteryPresent; int mLastBatteryLevel; int mLastBatteryVoltage; int mLastBatteryTemperature; boolean mLastBatteryLevelCritical;

private int mLowBatteryWarningLevel; private int mLowBatteryCloseWarningLevel; private int mPlugType; private int mLastPlugType = -1; // Extra state so we can detect first run private long mDischargeStartTime; private int mDischargeStartLevel; private boolean mSentLowBatteryBroadcast = false; public BatteryService(Context context) { mContext = context; mBatteryStats = BatteryStatsService.getService(); mLowBatteryWarningLevel = mContext.getResources().getInteger( com.android.internal.R.integer.config_lowBatteryWarningLevel); mLowBatteryCloseWarningLevel = mContext.getResources().getInteger( com.android.internal.R.integer.config_lowBatteryCloseWarningLevel); mUEventObserver.startObserving("SUBSYSTEM=power_supply"); // set initial status update(); } final boolean isPowered() { // assume we are powered if battery state is unknown so the "stay on while plugged in" option will work. return (mAcOnline || mUsbOnline || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN); } final boolean isPowered(int plugTypeSet) { // assume we are powered if battery state is unknown so // the "stay on while plugged in" option will work. if (mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) { return true; } if (plugTypeSet == 0) { return false; } int plugTypeBit = 0; if (mAcOnline) { plugTypeBit |= BatteryManager.BATTERY_PLUGGED_AC; } if (mUsbOnline) { plugTypeBit |= BatteryManager.BATTERY_PLUGGED_USB; } return (plugTypeSet & plugTypeBit) != 0; }

final int getPlugType() { return mPlugType; } private UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { update(); } }; // returns battery level as a percentage final int getBatteryLevel() { return mBatteryLevel; } void systemReady() { // check our power situation now that it is safe to display the shutdown dialog. shutdownIfNoPower(); shutdownIfOverTemp(); } private final void shutdownIfNoPower() { // shut down gracefully if our battery is critically low and we are not powered. // wait until the system has booted before attempting to display the shutdown dialog. if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) { Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } } private final void shutdownIfOverTemp() { // shut down gracefully if temperature is too high (> 68.0C) // wait until the system has booted before attempting to display the shutdown dialog. if (mBatteryTemperature > 680 && ActivityManagerNative.isSystemReady()) { Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } } private native void native_update(); private synchronized final void update() { native_update(); boolean logOutlier = false; long dischargeDuration = 0; shutdownIfNoPower(); shutdownIfOverTemp(); mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL; if (mAcOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_AC; } else if (mUsbOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_USB; } else { mPlugType = BATTERY_PLUGGED_NONE; } if (mBatteryStatus != mLastBatteryStatus || mBatteryHealth != mLastBatteryHealth || mBatteryPresent != mLastBatteryPresent || mBatteryLevel != mLastBatteryLevel || mPlugType != mLastPlugType || mBatteryVoltage != mLastBatteryVoltage || mBatteryTemperature != mLastBatteryTemperature) { if (mPlugType != mLastPlugType) { if (mLastPlugType == BATTERY_PLUGGED_NONE) { // discharging -> charging // There's no value in this data unless we've discharged at least once and the // battery level has changed; so don't log until it does. if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryLevel) { dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime; logOutlier = true;

EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration, mDischargeStartLevel, mBatteryLevel); // make sure we see a discharge event before logging again mDischargeStartTime = 0; } } else if (mPlugType == BATTERY_PLUGGED_NONE) { // charging -> discharging or we just powered up mDischargeStartTime = SystemClock.elapsedRealtime(); mDischargeStartLevel = mBatteryLevel; } } if (mBatteryStatus != mLastBatteryStatus || mBatteryHealth != mLastBatteryHealth || mBatteryPresent != mLastBatteryPresent || mPlugType != mLastPlugType) { EventLog.writeEvent(EventLogTags.BATTERY_STATUS, mBatteryStatus, mBatteryHealth, mBatteryPresent ? 1 : 0, mPlugType, mBatteryTechnology); } if (mBatteryLevel != mLastBatteryLevel || mBatteryVoltage != mLastBatteryVoltage || mBatteryTemperature != mLastBatteryTemperature) { EventLog.writeEvent(EventLogTags.BATTERY_LEVEL, mBatteryLevel, mBatteryVoltage, mBatteryTemperature); } if (mBatteryLevel != mLastBatteryLevel && mPlugType == BATTERY_PLUGGED_NONE) { // If the battery level has changed and we are on battery, update the current level. // This is used for discharge cycle tracking so this shouldn't be updated while the // battery is charging. try { mBatteryStats.recordCurrentLevel(mBatteryLevel); } catch (RemoteException e) { // Should never happen. } } if (mBatteryLevelCritical && !mLastBatteryLevelCritical && mPlugType == BATTERY_PLUGGED_NONE) { // We want to make sure we log discharge cycle outliers // if the battery is about to die. dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime; logOutlier = true; } final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE; final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE; /* The ACTION_BATTERY_LOW broadcast is sent in these situations: * - is just un-plugged (previously was plugged) and battery level is * less than or equal to WARNING, or * - is not plugged and battery level falls to WARNING boundary * (becomes <= mLowBatteryWarningLevel). */ final boolean sendBatteryLow = !plugged && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN && mBatteryLevel <= mLowBatteryWarningLevel && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel); sendIntent(); // Separate broadcast is sent for power connected / not connected // since the standard intent will not wake any applications and some // applications may want to have smart behavior based on this. Intent statusIntent = new Intent(); statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); if (mPlugType != 0 && mLastPlugType == 0) { statusIntent.setAction(Intent.ACTION_POWER_CONNECTED); mContext.sendBroadcast(statusIntent); } else if (mPlugType == 0 && mLastPlugType != 0) { statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED); mContext.sendBroadcast(statusIntent); } if (sendBatteryLow) { mSentLowBatteryBroadcast = true; statusIntent.setAction(Intent.ACTION_BATTERY_LOW); mContext.sendBroadcast(statusIntent); } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) { mSentLowBatteryBroadcast = false; statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);

mContext.sendBroadcast(statusIntent); } // This needs to be done after sendIntent() so that we get the lastest battery stats. if (logOutlier && dischargeDuration != 0) { logOutlier(dischargeDuration); } mLastBatteryStatus = mBatteryStatus; mLastBatteryHealth = mBatteryHealth; mLastBatteryPresent = mBatteryPresent; mLastBatteryLevel = mBatteryLevel; mLastPlugType = mPlugType; mLastBatteryVoltage = mBatteryVoltage; mLastBatteryTemperature = mBatteryTemperature; mLastBatteryLevelCritical = mBatteryLevelCritical; } } private final void sendIntent() { // Pack up the values and broadcast them to everyone Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING); try { mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE, mBatteryLevel); } catch (RemoteException e) { // Should never happen. } int icon = getIcon(mBatteryLevel); intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryStatus); intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryHealth); intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryPresent); intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryLevel); intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE); intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon); intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType); intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryVoltage); intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryTemperature); intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology); if (false) { Slog.d(TAG, "updateBattery level:" + mBatteryLevel + " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus + " health:" + mBatteryHealth + " present:" + mBatteryPresent + " voltage: " + mBatteryVoltage + " temperature: " + mBatteryTemperature + " technology: " + mBatteryTechnology + " AC powered:" + mAcOnline + " USB powered:" + mUsbOnline + " icon:" + icon ); } ActivityManagerNative.broadcastStickyIntent(intent, null); } private final void logBatteryStats() { IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME); if (batteryInfoService == null) return; DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE); if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return; File dumpFile = null; FileOutputStream dumpStream = null; try { // dump the service to a file dumpFile = new File(DUMPSYS_DATA_PATH + BATTERY_STATS_SERVICE_NAME + ".dump"); dumpStream = new FileOutputStream(dumpFile); batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS); dumpStream.getFD().sync(); // add dump file to drop box db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT); } catch (RemoteException e) { Slog.e(TAG, "failed to dump battery service", e); } catch (IOException e) { Slog.e(TAG, "failed to write dumpsys file", e); } finally {

// make sure we clean up if (dumpStream != null) { try { dumpStream.close(); } catch (IOException e) { Slog.e(TAG, "failed to close dumpsys output stream"); } } if (dumpFile != null && !dumpFile.delete()) { Slog.e(TAG, "failed to delete temporary dumpsys file: " + dumpFile.getAbsolutePath()); } } } private final void logOutlier(long duration) { ContentResolver cr = mContext.getContentResolver(); String dischargeThresholdString = Settings.Secure.getString(cr, Settings.Secure.BATTERY_DISCHARGE_THRESHOLD); String durationThresholdString = Settings.Secure.getString(cr, Settings.Secure.BATTERY_DISCHARGE_DURATION_THRESHOLD); if (dischargeThresholdString != null && durationThresholdString != null) { try { long durationThreshold = Long.parseLong(durationThresholdString); int dischargeThreshold = Integer.parseInt(dischargeThresholdString); if (duration <= durationThreshold && mDischargeStartLevel - mBatteryLevel >= dischargeThreshold) { // If the discharge cycle is bad enough we want to know about it. logBatteryStats(); } if (LOCAL_LOGV) Slog.v(TAG, "duration threshold: " + durationThreshold + " discharge threshold: " + dischargeThreshold); if (LOCAL_LOGV) Slog.v(TAG, "duration: " + duration + " discharge: " + (mDischargeStartLevel - mBatteryLevel)); } catch (NumberFormatException e) { Slog.e(TAG, "Invalid DischargeThresholds GService string: " + durationThresholdString + " or " + dischargeThresholdString); return; } } } private final int getIcon(int level) { if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) { return com.android.internal.R.drawable.stat_sys_battery_charge; } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING || mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING || mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) { return com.android.internal.R.drawable.stat_sys_battery; } else { return com.android.internal.R.drawable.stat_sys_battery_unknown; } } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump Battery service from from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); return; } synchronized (this) { pw.println("Current Battery Service state:"); pw.println(" AC powered: " + mAcOnline); pw.println(" USB powered: " + mUsbOnline); pw.println(" status: " + mBatteryStatus); pw.println(" health: " + mBatteryHealth); pw.println(" present: " + mBatteryPresent); pw.println(" level: " + mBatteryLevel); pw.println(" scale: " + BATTERY_SCALE); pw.println(" voltage:" + mBatteryVoltage); pw.println(" temperature: " + mBatteryTemperature); pw.println(" technology: " + mBatteryTechnology); } }

ATTACHMENT C

/* * 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.net; import android.os.Parcelable; import android.os.Parcel; import java.util.EnumMap; /** * Describes the status of a network interface of a given type * (currently either Mobile or Wifi). */ public class NetworkInfo implements Parcelable { /** * Coarse-grained network state. This is probably what most applications should * use, rather than {@link android.net.NetworkInfo.DetailedState DetailedState}. * The mapping between the two is as follows: * <br/><br/> * <table> * <tr><td><b>Detailed state</b></td><td><b>Coarse-grained state</b></td></tr> * <tr><td><code>IDLE</code></td><td><code>DISCONNECTED</code></td></tr> * <tr><td><code>SCANNING</code></td><td><code>CONNECTING</code></td></tr> * <tr><td><code>CONNECTING</code></td><td><code>CONNECTING</code></td></tr> * <tr><td><code>AUTHENTICATING</code></td><td><code>CONNECTING</code></td></tr> * <tr><td><code>CONNECTED</code></td><td<code>CONNECTED</code></td></tr> * <tr><td><code>DISCONNECTING</code></td><td><code>DISCONNECTING</code></td></tr> * <tr><td><code>DISCONNECTED</code></td><td><code>DISCONNECTED</code></td></tr> * <tr><td><code>UNAVAILABLE</code></td><td><code>DISCONNECTED</code></td></tr> * <tr><td><code>FAILED</code></td><td><code>DISCONNECTED</code></td></tr> * </table> */ public enum State { CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, UNKNOWN } /** * The fine-grained state of a network connection. This level of detail * is probably of interest to few applications. Most should use * {@link android.net.NetworkInfo.State State} instead. */ public enum DetailedState { /** Ready to start data connection setup. */ IDLE, /** Searching for an available access point. */ SCANNING, /** Currently setting up data connection. */ CONNECTING, /** Network link established, performing authentication. */ AUTHENTICATING, /** Awaiting response from DHCP server in order to assign IP address information. */ OBTAINING_IPADDR, /** IP traffic should be available. */

CONNECTED, /** IP traffic is suspended */ SUSPENDED, /** Currently tearing down data connection. */ DISCONNECTING, /** IP traffic not available. */ DISCONNECTED, /** Attempt to connect failed. */ FAILED } /** * This is the map described in the Javadoc comment above. The positions * of the elements of the array must correspond to the ordinal values * of <code>DetailedState</code>. */ private static final EnumMap<DetailedState, State> stateMap = new EnumMap<DetailedState, State>(DetailedState.class); static { stateMap.put(DetailedState.IDLE, State.DISCONNECTED); stateMap.put(DetailedState.SCANNING, State.DISCONNECTED); stateMap.put(DetailedState.CONNECTING, State.CONNECTING); stateMap.put(DetailedState.AUTHENTICATING, State.CONNECTING); stateMap.put(DetailedState.OBTAINING_IPADDR, State.CONNECTING); stateMap.put(DetailedState.CONNECTED, State.CONNECTED); stateMap.put(DetailedState.SUSPENDED, State.SUSPENDED); stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING); stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED); stateMap.put(DetailedState.FAILED, State.DISCONNECTED); } private int mNetworkType; private int mSubtype; private String mTypeName; private String mSubtypeName; private State mState; private DetailedState mDetailedState; private String mReason; private String mExtraInfo; private boolean mIsFailover; private boolean mIsRoaming; /** * Indicates whether network connectivity is possible: */ private boolean mIsAvailable; /** * @param type network type * @deprecated * @hide because this constructor was only meant for internal use (and * has now been superseded by the package-private constructor below). */ public NetworkInfo(int type) {} NetworkInfo(int type, int subtype, String typeName, String subtypeName) { if (!ConnectivityManager.isNetworkTypeValid(type)) { throw new IllegalArgumentException("Invalid network type: " + type); } mNetworkType = type; mSubtype = subtype; mTypeName = typeName; mSubtypeName = subtypeName; setDetailedState(DetailedState.IDLE, null, null); mState = State.UNKNOWN; mIsAvailable = false; // until we're told otherwise, assume unavailable mIsRoaming = false; }

/** * Reports * info in * @return */ public int return }

the type of network (currently mobile or Wi-Fi) to which the this object pertains. the network type getType() { mNetworkType;

/** * Return a network-type-specific integer describing the subtype * of the network. * @return the network subtype */ public int getSubtype() { return mSubtype; } void setSubtype(int subtype, String subtypeName) { mSubtype = subtype; mSubtypeName = subtypeName; } /** * Return a human-readable name describe the type of the network, * for example "WIFI" or "MOBILE". * @return the name of the network type */ public String getTypeName() { return mTypeName; } /** * Return a human-readable name describing the subtype of the network. * @return the name of the network subtype */ public String getSubtypeName() { return mSubtypeName; } /** * Indicates whether network connectivity exists or is in the process * of being established. This is good for applications that need to * do anything related to the network other than read or write data. * For the latter, call {@link #isConnected()} instead, which guarantees * that the network is fully usable. * @return {@code true} if network connectivity exists or is in the process * of being established, {@code false} otherwise. */ public boolean isConnectedOrConnecting() { return mState == State.CONNECTED || mState == State.CONNECTING; } /** * Indicates whether network connectivity exists and it is possible to establish * connections and pass data. * @return {@code true} if network connectivity exists, {@code false} otherwise. */ public boolean isConnected() { return mState == State.CONNECTED; } /** * Indicates whether network connectivity is possible. A network is unavailable * when a persistent or semi-persistent condition prevents the possibility * of connecting to that network. Examples include * <ul>

* <li>The device is out of the coverage area for any network of this type.</li> * <li>The device is on a network other than the home network (i.e., roaming), and * data roaming has been disabled.</li> * <li>The device's radio is turned off, e.g., because airplane mode is enabled.</li> * </ul> * @return {@code true} if the network is available, {@code false} otherwise */ public boolean isAvailable() { return mIsAvailable; } /** * Sets if the network is available, ie, if the connectivity is possible. * @param isAvailable the new availability value. * * @hide */ public void setIsAvailable(boolean isAvailable) { mIsAvailable = isAvailable; } /** * Indicates whether the current attempt to connect to the network * resulted from the ConnectivityManager trying to fail over to this * network following a disconnect from another network. * @return {@code true} if this is a failover attempt, {@code false} * otherwise. */ public boolean isFailover() { return mIsFailover; } /** * Set the failover boolean. * @param isFailover {@code true} to mark the current connection attempt * as a failover. * @hide */ public void setFailover(boolean isFailover) { mIsFailover = isFailover; } /** * Indicates whether the device is currently roaming on this network. * When {@code true}, it suggests that use of data on this network * may incur extra costs. * @return {@code true} if roaming is in effect, {@code false} otherwise. */ public boolean isRoaming() { return mIsRoaming; } void setRoaming(boolean isRoaming) { mIsRoaming = isRoaming; } /** * Reports the current coarse-grained state of the network. * @return the coarse-grained state */ public State getState() { return mState; } /** * Reports the current fine-grained state of the network. * @return the fine-grained state */

public DetailedState getDetailedState() { return mDetailedState; } /** * Sets the fine-grained state of the network. * @param detailedState the {@link DetailedState}. * @param reason a {@code String} indicating the reason for the state change, * if one was supplied. May be {@code null}. * @param extraInfo an optional {@code String} providing addditional network state * information passed up from the lower networking layers. */ void setDetailedState(DetailedState detailedState, String reason, String extraInfo) { this.mDetailedState = detailedState; this.mState = stateMap.get(detailedState); this.mReason = reason; this.mExtraInfo = extraInfo; } /** * Report the reason an attempt to establish connectivity failed, * if one is available. * @return the reason for failure, or null if not available */ public String getReason() { return mReason; } /** * Report the extra information about the network state, if any was * provided by the lower networking layers., * if one is available. * @return the extra information, or null if not available */ public String getExtraInfo() { return mExtraInfo; } @Override public String toString() { StringBuilder builder = new StringBuilder("NetworkInfo: "); builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()). append("], state: ").append(mState).append("/").append(mDetailedState). append(", reason: ").append(mReason == null ? "(unspecified)" : mReason). append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo). append(", roaming: ").append(mIsRoaming). append(", failover: ").append(mIsFailover). append(", isAvailable: ").append(mIsAvailable); return builder.toString(); } /** * Implement the Parcelable interface * @hide */ public int describeContents() { return 0; } /** * Implement the Parcelable interface. * @hide */ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mNetworkType); dest.writeInt(mSubtype); dest.writeString(mTypeName); dest.writeString(mSubtypeName);

dest.writeString(mState.name()); dest.writeString(mDetailedState.name()); dest.writeInt(mIsFailover ? 1 : 0); dest.writeInt(mIsAvailable ? 1 : 0); dest.writeInt(mIsRoaming ? 1 : 0); dest.writeString(mReason); dest.writeString(mExtraInfo); } /** * Implement the Parcelable interface. * @hide */ public static final Creator<NetworkInfo> CREATOR = new Creator<NetworkInfo>() { public NetworkInfo createFromParcel(Parcel in) { int netType = in.readInt(); int subtype = in.readInt(); String typeName = in.readString(); String subtypeName = in.readString(); NetworkInfo netInfo = new NetworkInfo(netType, subtype, typeName, subtypeName); netInfo.mState = State.valueOf(in.readString()); netInfo.mDetailedState = DetailedState.valueOf(in.readString()); netInfo.mIsFailover = in.readInt() != 0; netInfo.mIsAvailable = in.readInt() != 0; netInfo.mIsRoaming = in.readInt() != 0; netInfo.mReason = in.readString(); netInfo.mExtraInfo = in.readString(); return netInfo; } public NetworkInfo[] newArray(int size) { return new NetworkInfo[size]; } }; }

ATTACHMENT D

/* * 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.content; import import import import import import import import import import import import import import import import import import import import android.content.pm.ApplicationInfo; android.content.pm.PackageManager; android.content.res.AssetManager; android.content.res.Resources; android.content.res.TypedArray; android.database.sqlite.SQLiteDatabase; android.database.sqlite.SQLiteDatabase.CursorFactory; android.graphics.Bitmap; android.graphics.drawable.Drawable; android.net.Uri; android.os.Bundle; android.os.Handler; android.os.Looper; android.util.AttributeSet; java.io.File; java.io.FileInputStream; java.io.FileNotFoundException; java.io.FileOutputStream; java.io.IOException; java.io.InputStream;

/** * Interface to global information about an application environment. This is * an abstract class whose implementation is provided by * the Android system. It * allows access to application-specific resources and classes, as well as * up-calls for application-level operations such as launching activities, * broadcasting and receiving intents, etc. */ public abstract class Context { /** * File creation mode: the default mode, where the created file can only * be accessed by the calling application (or all applications sharing the * same user ID). * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE */ public static final int MODE_PRIVATE = 0x0000; /** * File creation mode: allow all other applications to have read access * to the created file. * @see #MODE_PRIVATE * @see #MODE_WORLD_WRITEABLE */ public static final int MODE_WORLD_READABLE = 0x0001; /** * File creation mode: allow all other applications to have write access * to the created file. * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE */ public static final int MODE_WORLD_WRITEABLE = 0x0002; /** * File creation mode: for use with {@link #openFileOutput}, if the file

* already exists then write data to the end of the existing file * instead of erasing it. * @see #openFileOutput */ public static final int MODE_APPEND = 0x8000; /** * Flag for {@link #bindService}: automatically create the service as long * as the binding exists. Note that while this will create the service, * its {@link android.app.Service#onStart} method will still only be called due to an * explicit call to {@link #startService}. Even without that, though, * this still provides you with access to the service object while the * service is created. * * <p>Specifying this flag also tells the system to treat the service * as being as important as your own process -- that is, when deciding * which process should be killed to free memory, the service will only * be considered a candidate as long as the processes of any such bindings * is also a candidate to be killed. This is to avoid situations where * the service is being continually created and killed due to low memory. */ public static final int BIND_AUTO_CREATE = 0x0001; /** * Flag for {@link #bindService}: include debugging help for mismatched * calls to unbind. When this flag is set, the callstack of the following * {@link #unbindService} call is retained, to be printed if a later * incorrect unbind call is made. Note that doing this requires retaining * information about the binding that was made for the lifetime of the app, * resulting in a leak -- this should only be used for debugging. */ public static final int BIND_DEBUG_UNBIND = 0x0002; /** * Flag for {@link #bindService}: don't allow this binding to raise * the target service's process to the foreground scheduling priority. * It will still be raised to the at least the same memory priority * as the client (so that its process will not be killable in any * situation where the client is not killable), but for CPU scheduling * purposes it may be left in the background. This only has an impact * in the situation where the binding client is a foreground process * and the target service is in a background process. */ public static final int BIND_NOT_FOREGROUND = 0x0004; /** Return an AssetManager instance for your application's package. */ public abstract AssetManager getAssets(); /** Return a Resources instance for your application's package. */ public abstract Resources getResources(); /** Return PackageManager instance to find global package information. */ public abstract PackageManager getPackageManager(); /** Return a ContentResolver instance for your application's package. */ public abstract ContentResolver getContentResolver(); /** * Return the Looper for the main thread of the current process. This is * the thread used to dispatch calls to application components (activities, * services, etc). */ public abstract Looper getMainLooper(); /** * Return the context of the single, global Application object of the * current process. This generally should only be used if you need a * Context whose lifecycle is separate from the current context, that is * tied to the lifetime of the process rather than the current component. * * <p>Consider for example how this interacts with * {@ #registerReceiver(BroadcastReceiver, IntentFilter)}: * <ul>

* <li> <p>If used from an Activity context, the receiver is being registered * within that activity. This means that you are expected to unregister * before the activity is done being destroyed; in fact if you do not do * so, the framework will clean up your leaked registration as it removes * the activity and log an error. Thus, if you use the Activity context * to register a receiver that is static (global to the process, not * associated with an Activity instance) then that registration will be * removed on you at whatever point the activity you used is destroyed. * <li> <p>If used from the Context returned here, the receiver is being * registered with the global state associated with your application. Thus * it will never be unregistered for you. This is necessary if the receiver * is associated with static data, not a particular component. However * using the ApplicationContext elsewhere can easily lead to serious leaks * if you forget to unregister, unbind, etc. * </ul> */ public abstract Context getApplicationContext(); /** * Return a localized, styled CharSequence from the application's package's * default string table. * * @param resId Resource id for the CharSequence text */ public final CharSequence getText(int resId) { return getResources().getText(resId); } /** * Return a localized string from the application's package's * default string table. * * @param resId Resource id for the string */ public final String getString(int resId) { return getResources().getString(resId); } /** * Return a localized formatted string from the application's package's * default string table, substituting the format arguments as defined in * {@link java.util.Formatter} and {@link java.lang.String#format}. * * @param resId Resource id for the format string * @param formatArgs The format arguments that will be used for substitution. */ public final String getString(int resId, Object... formatArgs) { return getResources().getString(resId, formatArgs); } /** * Set the base theme for this context. Note that this should be called * before any views are instantiated in the Context (for example before * calling {@link android.app.Activity#setContentView} or * {@link android.view.LayoutInflater#inflate}). * * @param resid The style resource describing the theme. */ public abstract void setTheme(int resid); /** * Return the Theme object associated with this Context. */ public abstract Resources.Theme getTheme(); /** * Retrieve styled attribute information in this Context's theme. * {@link Resources.Theme#obtainStyledAttributes(int[])} * for more information. * * @see Resources.Theme#obtainStyledAttributes(int[]) */

See

public final TypedArray obtainStyledAttributes( int[] attrs) { return getTheme().obtainStyledAttributes(attrs); } /** * Retrieve styled attribute information in this Context's theme. See * {@link Resources.Theme#obtainStyledAttributes(int, int[])} * for more information. * * @see Resources.Theme#obtainStyledAttributes(int, int[]) */ public final TypedArray obtainStyledAttributes( int resid, int[] attrs) throws Resources.NotFoundException { return getTheme().obtainStyledAttributes(resid, attrs); } /** * Retrieve styled attribute information in this Context's theme. See * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)} * for more information. * * @see Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int) */ public final TypedArray obtainStyledAttributes( AttributeSet set, int[] attrs) { return getTheme().obtainStyledAttributes(set, attrs, 0, 0); } /** * Retrieve styled attribute information in this Context's theme. See * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)} * for more information. * * @see Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int) */ public final TypedArray obtainStyledAttributes( AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) { return getTheme().obtainStyledAttributes( set, attrs, defStyleAttr, defStyleRes); } /** * Return a class loader you can use to retrieve classes in this package. */ public abstract ClassLoader getClassLoader(); /** Return the name of this application's package. */ public abstract String getPackageName(); /** Return the full application info for this context's package. */ public abstract ApplicationInfo getApplicationInfo(); /** * Return the full path to this context's primary Android package. * The Android package is a ZIP file which contains the application's * primary resources. * * <p>Note: this is not generally useful for applications, since they should * not be directly accessing the file system. * * @return String Path to the resources. */ public abstract String getPackageResourcePath(); /** * Return the full path to this context's primary Android package. * The Android package is a ZIP file which contains application's * primary code and assets. * * <p>Note: this is not generally useful for applications, since they should * not be directly accessing the file system. *

* @return String Path to the code and assets. */ public abstract String getPackageCodePath(); /** * {@hide} * Return the full path to the shared prefs file for the given prefs group name. * * <p>Note: this is not generally useful for applications, since they should * not be directly accessing the file system. */ public abstract File getSharedPrefsFile(String name); /** * Retrieve and hold the contents of the preferences file 'name', returning * a SharedPreferences through which you can retrieve and modify its * values. Only one instance of the SharedPreferences object is returned * to any callers for the same name, meaning they will see each other's * edits as soon as they are made. * * @param name Desired preferences file. If a preferences file by this name * does not exist, it will be created when you retrieve an * editor (SharedPreferences.edit()) and then commit changes (Editor.commit()). * @param mode Operating mode. Use 0 or {@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. * * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE */ public abstract SharedPreferences getSharedPreferences(String name, int mode); /** * Open a private file associated with this Context's application package * for reading. * * @param name The name of the file to open; can not contain path * separators. * * @return FileInputStream Resulting input stream. * * @see #openFileOutput * @see #fileList * @see #deleteFile * @see java.io.FileInputStream#FileInputStream(String) */ public abstract FileInputStream openFileInput(String name) throws FileNotFoundException; /** * Open a private file associated with this Context's application package * for writing. Creates the file if it doesn't already exist. * * @param name The name of the file to open; can not contain path * separators. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the * default operation, {@link #MODE_APPEND} to append to an existing file, * {@link #MODE_WORLD_READABLE} and {@link #MODE_WORLD_WRITEABLE} to control * permissions. * * @return FileOutputStream Resulting output stream. * * @see #MODE_APPEND * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE * @see #openFileInput * @see #fileList

* @see #deleteFile * @see java.io.FileOutputStream#FileOutputStream(String) */ public abstract FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException; /** * Delete the given private file associated with this Context's * application package. * * @param name The name of the file to delete; can not contain path * separators. * * @return True if the file was successfully deleted; else * false. * * @see #openFileInput * @see #openFileOutput * @see #fileList * @see java.io.File#delete() */ public abstract boolean deleteFile(String name); /** * Returns the absolute path on the filesystem where a file created with * {@link #openFileOutput} is stored. * * @param name The name of the file for which you would like to get * its path. * * @return Returns an absolute path to the given file. * * @see #openFileOutput * @see #getFilesDir * @see #getDir */ public abstract File getFileStreamPath(String name); /** * Returns the absolute path to the directory on the filesystem where * files created with {@link #openFileOutput} are stored. * * @return Returns the path of the directory holding application files. * * @see #openFileOutput * @see #getFileStreamPath * @see #getDir */ public abstract File getFilesDir(); /** * Returns the absolute path to the directory on the external filesystem * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory() * Environment.getExternalStorageDirectory()}) where the application can * place persistent files it owns. These files are private to the * applications, and not typically visible to the user as media. * * <p>This is like {@link #getFilesDir()} in that these * files will be deleted when the application is uninstalled, however there * are some important differences: * * <ul> * <li>External files are not always available: they will disappear if the * user mounts the external storage on a computer or removes it. See the * APIs on {@link android.os.Environment} for information in the storage state. * <li>There is no security enforced with these files. All applications * can read and write files placed here. * </ul> * * <p>Here is an example of typical code to manipulate a file in * an application's private storage:</p> * * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java

* private_file} * * <p>If you supply a non-null <var>type</var> to this function, the returned * file will be a path to a sub-directory of the given type. Though these files * are not automatically scanned by the media scanner, you can explicitly * add them to the media database with * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], * OnScanCompletedListener) MediaScannerConnection.scanFile}. * Note that this is not the same as * {@link android.os.Environment#getExternalStoragePublicDirectory * Environment.getExternalStoragePublicDirectory()}, which provides * directories of media shared by all applications. The * directories returned here are * owned by the application, and their contents will be removed when the * application is uninstalled. Unlike * {@link android.os.Environment#getExternalStoragePublicDirectory * Environment.getExternalStoragePublicDirectory()}, the directory * returned here will be automatically created for you. * * <p>Here is an example of typical code to manipulate a picture in * an application's private storage and add it to the media database:</p> * * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java * private_picture} * * @param type The type of files directory to return. May be null for * the root of the files directory or one of * the following Environment constants for a subdirectory: * {@link android.os.Environment#DIRECTORY_MUSIC}, * {@link android.os.Environment#DIRECTORY_PODCASTS}, * {@link android.os.Environment#DIRECTORY_RINGTONES}, * {@link android.os.Environment#DIRECTORY_ALARMS}, * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, * {@link android.os.Environment#DIRECTORY_PICTURES}, or * {@link android.os.Environment#DIRECTORY_MOVIES}. * * @return Returns the path of the directory holding application files * on external storage. Returns null if external storage is not currently * mounted so it could not ensure the path exists; you will need to call * this method again when it is available. * * @see #getFilesDir * @see android.os.Environment#getExternalStoragePublicDirectory */ public abstract File getExternalFilesDir(String type); /** * Returns the absolute path to the application specific cache directory * on the filesystem. These files will be ones that get deleted first when the * device runs low on storage. * There is no guarantee when these files will be deleted. * * <strong>Note: you should not <em>rely</em> on the system deleting these * files for you; you should always have a reasonable maximum, such as 1 MB, * for the amount of space you consume with cache files, and prune those * files when exceeding that space.</strong> * * @return Returns the path of the directory holding application cache files. * * @see #openFileOutput * @see #getFileStreamPath * @see #getDir */ public abstract File getCacheDir(); /** * Returns the absolute path to the directory on the external filesystem * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory() * Environment.getExternalStorageDirectory()} where the application can * place cache files it owns. * * <p>This is like {@link #getCacheDir()} in that these * files will be deleted when the application is uninstalled, however there

* are some important differences: * * <ul> * <li>The platform does not monitor the space available in external storage, * and thus will not automatically delete these files. Note that you should * be managing the maximum space you will use for these anyway, just like * with {@link #getCacheDir()}. * <li>External files are not always available: they will disappear if the * user mounts the external storage on a computer or removes it. See the * APIs on {@link android.os.Environment} for information in the storage state. * <li>There is no security enforced with these files. All applications * can read and write files placed here. * </ul> * * @return Returns the path of the directory holding application cache files * on external storage. Returns null if external storage is not currently * mounted so it could not ensure the path exists; you will need to call * this method again when it is available. * * @see #getCacheDir */ public abstract File getExternalCacheDir(); /** * Returns an array of strings naming the private files associated with * this Context's application package. * * @return Array of strings naming the private files. * * @see #openFileInput * @see #openFileOutput * @see #deleteFile */ public abstract String[] fileList(); /** * Retrieve, creating if needed, a new directory in which the application * can place its own custom data files. You can use the returned File * object to create and access files in this directory. Note that files * created through a File object will only be accessible by your own * application; you can only set the mode of the entire directory, not * of individual files. * * @param name Name of the directory to retrieve. This is a directory * that is created as part of your application data. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the * default operation, {@link #MODE_WORLD_READABLE} and * {@link #MODE_WORLD_WRITEABLE} to control permissions. * * @return Returns a File object for the requested directory. The directory * will have been created if it does not already exist. * * @see #openFileOutput(String, int) */ public abstract File getDir(String name, int mode); /** * Open a new private SQLiteDatabase associated with this Context's * application package. Create the database file if it doesn't exist. * * @param name The name (unique in the application package) of the database. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the * default operation, {@link #MODE_WORLD_READABLE} * and {@link #MODE_WORLD_WRITEABLE} to control permissions. * @param factory An optional factory class that is called to instantiate a * cursor when query is called. * * @return The contents of a newly created database with the given name. * @throws android.database.sqlite.SQLiteException if the database file could not be opened. * * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE

* @see #deleteDatabase */ public abstract SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory); /** * Delete an existing private SQLiteDatabase associated with this Context's * application package. * * @param name The name (unique in the application package) of the * database. * * @return True if the database was successfully deleted; else false. * * @see #openOrCreateDatabase */ public abstract boolean deleteDatabase(String name); /** * Returns the absolute path on the filesystem where a database created with * {@link #openOrCreateDatabase} is stored. * * @param name The name of the database for which you would like to get * its path. * * @return Returns an absolute path to the given database. * * @see #openOrCreateDatabase */ public abstract File getDatabasePath(String name); /** * Returns an array of strings naming the private databases associated with * this Context's application package. * * @return Array of strings naming the private databases. * * @see #openOrCreateDatabase * @see #deleteDatabase */ public abstract String[] databaseList(); /** * @deprecated Use {@link android.app.WallpaperManager#getDrawable * WallpaperManager.get()} instead. */ @Deprecated public abstract Drawable getWallpaper(); /** * @deprecated Use {@link android.app.WallpaperManager#peekDrawable * WallpaperManager.peek()} instead. */ @Deprecated public abstract Drawable peekWallpaper(); /** * @deprecated Use {@link android.app.WallpaperManager#getDesiredMinimumWidth() * WallpaperManager.getDesiredMinimumWidth()} instead. */ @Deprecated public abstract int getWallpaperDesiredMinimumWidth(); /** * @deprecated Use {@link android.app.WallpaperManager#getDesiredMinimumHeight() * WallpaperManager.getDesiredMinimumHeight()} instead. */ @Deprecated public abstract int getWallpaperDesiredMinimumHeight(); /** * @deprecated Use {@link android.app.WallpaperManager#setBitmap(Bitmap) * WallpaperManager.set()} instead.

*/ @Deprecated public abstract void setWallpaper(Bitmap bitmap) throws IOException; /** * @deprecated Use {@link android.app.WallpaperManager#setStream(InputStream) * WallpaperManager.set()} instead. */ @Deprecated public abstract void setWallpaper(InputStream data) throws IOException; /** * @deprecated Use {@link android.app.WallpaperManager#clear * WallpaperManager.clear()} instead. */ @Deprecated public abstract void clearWallpaper() throws IOException; /** * Launch a new activity. You will not receive any information about when * the activity exits. * * <p>Note that if this method is being called from outside of an * {@link android.app.Activity} Context, then the Intent must include * the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag. This is because, * without being started from an existing Activity, there is no existing * task in which to place the new activity and thus it needs to be placed * in its own separate task. * * <p>This method throws {@link ActivityNotFoundException} * if there was no Activity found to run the given Intent. * * @param intent The description of the activity to start. * * @throws ActivityNotFoundException * * @see PackageManager#resolveActivity */ public abstract void startActivity(Intent intent); /** * Like {@link #startActivity(Intent)}, but taking a IntentSender * to start. If the IntentSender is for an activity, that activity will be started * as if you had called the regular {@link #startActivity(Intent)} * here; otherwise, its associated action will be executed (such as * sending a broadcast) as if you had called * {@link IntentSender#sendIntent IntentSender.sendIntent} on it. * * @param intent The IntentSender to launch. * @param fillInIntent If non-null, this will be provided as the * intent parameter to {@link IntentSender#sendIntent}. * @param flagsMask Intent flags in the original IntentSender that you * would like to change. * @param flagsValues Desired values for any bits set in * <var>flagsMask</var> * @param extraFlags Always set to 0. */ public abstract void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws IntentSender.SendIntentException; /** * Broadcast the given intent to all interested BroadcastReceivers. This * call is asynchronous; it returns immediately, and you will continue * executing while the receivers are run. No results are propagated from * receivers and receivers can not abort the broadcast. If you want * to allow receivers to propagate results or abort the broadcast, you must * send an ordered broadcast using * {@link #sendOrderedBroadcast(Intent, String)}. * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * * @param intent The Intent to broadcast; all receivers matching this

* Intent will receive the broadcast. * * @see android.content.BroadcastReceiver * @see #registerReceiver * @see #sendBroadcast(Intent, String) * @see #sendOrderedBroadcast(Intent, String) * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) */ public abstract void sendBroadcast(Intent intent); /** * Broadcast the given intent to all interested BroadcastReceivers, allowing * an optional required permission to be enforced. This * call is asynchronous; it returns immediately, and you will continue * executing while the receivers are run. No results are propagated from * receivers and receivers can not abort the broadcast. If you want * to allow receivers to propagate results or abort the broadcast, you must * send an ordered broadcast using * {@link #sendOrderedBroadcast(Intent, String)}. * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. * @param receiverPermission (optional) String naming a permissions that * a receiver must hold in order to receive your broadcast. * If null, no permission is required. * * @see android.content.BroadcastReceiver * @see #registerReceiver * @see #sendBroadcast(Intent) * @see #sendOrderedBroadcast(Intent, String) * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) */ public abstract void sendBroadcast(Intent intent, String receiverPermission); /** * Broadcast the given intent to all interested BroadcastReceivers, delivering * them one at a time to allow more preferred receivers to consume the * broadcast before it is delivered to less preferred receivers. This * call is asynchronous; it returns immediately, and you will continue * executing while the receivers are run. * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. * @param receiverPermission (optional) String naming a permissions that * a receiver must hold in order to receive your broadcast. * If null, no permission is required. * * @see android.content.BroadcastReceiver * @see #registerReceiver * @see #sendBroadcast(Intent) * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) */ public abstract void sendOrderedBroadcast(Intent intent, String receiverPermission); /** * Version of {@link #sendBroadcast(Intent)} that allows you to * receive data back from the broadcast. This is accomplished by * supplying your own BroadcastReceiver when calling, which will be * treated as a final receiver at the end of the broadcast -- its * {@link BroadcastReceiver#onReceive} method will be called with * the result values collected from the other receivers. The broadcast will * be serialized in the same way as calling * {@link #sendOrderedBroadcast(Intent, String)}. * * <p>Like {@link #sendBroadcast(Intent)}, this method is * asynchronous; it will return before * resultReceiver.onReceive() is called.

* * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. * @param receiverPermission String naming a permissions that * a receiver must hold in order to receive your broadcast. * If null, no permission is required. * @param resultReceiver Your own BroadcastReceiver to treat as the final * receiver of the broadcast. * @param scheduler A custom Handler with which to schedule the * resultReceiver callback; if null it will be * scheduled in the Context's main thread. * @param initialCode An initial value for the result code. Often * Activity.RESULT_OK. * @param initialData An initial value for the result data. Often * null. * @param initialExtras An initial value for the result extras. Often * null. * * @see #sendBroadcast(Intent) * @see #sendBroadcast(Intent, String) * @see #sendOrderedBroadcast(Intent, String) * @see #sendStickyBroadcast(Intent) * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) * @see android.content.BroadcastReceiver * @see #registerReceiver * @see android.app.Activity#RESULT_OK */ public abstract void sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras); /** * Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the * Intent you are sending stays around after the broadcast is complete, * so that others can quickly retrieve that data through the return * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In * all other ways, this behaves the same as * {@link #sendBroadcast(Intent)}. * * <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY} * permission in order to use this API. If you do not hold that * permission, {@link SecurityException} will be thrown. * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast, and the Intent will be held to * be re-broadcast to future receivers. * * @see #sendBroadcast(Intent) * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) */ public abstract void sendStickyBroadcast(Intent intent); /** * Version of {@link #sendStickyBroadcast} that allows you to * receive data back from the broadcast. This is accomplished by * supplying your own BroadcastReceiver when calling, which will be * treated as a final receiver at the end of the broadcast -- its * {@link BroadcastReceiver#onReceive} method will be called with * the result values collected from the other receivers. The broadcast will * be serialized in the same way as calling * {@link #sendOrderedBroadcast(Intent, String)}. * * <p>Like {@link #sendBroadcast(Intent)}, this method is * asynchronous; it will return before * resultReceiver.onReceive() is called. Note that the sticky data * stored is only the data you initially supply to the broadcast, not * the result of any changes made by the receivers. * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. *

* @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. * @param resultReceiver Your own BroadcastReceiver to treat as the final * receiver of the broadcast. * @param scheduler A custom Handler with which to schedule the * resultReceiver callback; if null it will be * scheduled in the Context's main thread. * @param initialCode An initial value for the result code. Often * Activity.RESULT_OK. * @param initialData An initial value for the result data. Often * null. * @param initialExtras An initial value for the result extras. Often * null. * * @see #sendBroadcast(Intent) * @see #sendBroadcast(Intent, String) * @see #sendOrderedBroadcast(Intent, String) * @see #sendStickyBroadcast(Intent) * @see android.content.BroadcastReceiver * @see #registerReceiver * @see android.app.Activity#RESULT_OK */ public abstract void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);

/** * Remove the data previously sent with {@link #sendStickyBroadcast}, * so that it is as if the sticky broadcast had never happened. * * <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY} * permission in order to use this API. If you do not hold that * permission, {@link SecurityException} will be thrown. * * @param intent The Intent that was previously broadcast. * * @see #sendStickyBroadcast */ public abstract void removeStickyBroadcast(Intent intent); /** * Register a BroadcastReceiver to be run in the main activity thread. The * <var>receiver</var> will be called with any broadcast Intent that * matches <var>filter</var>, in the main application thread. * * <p>The system may broadcast Intents that are "sticky" -- these stay * around after the broadcast as finished, to be sent to any later * registrations. If your IntentFilter matches one of these sticky * Intents, that Intent will be returned by this function * <strong>and</strong> sent to your <var>receiver</var> as if it had just * been broadcast. * * <p>There may be multiple sticky Intents that match <var>filter</var>, * in which case each of these will be sent to <var>receiver</var>. In * this case, only one of these can be returned directly by the function; * which of these that is returned is arbitrarily decided by the system. * * <p>If you know the Intent your are registering for is sticky, you can * supply null for your <var>receiver</var>. In this case, no receiver is * registered -- the function simply returns the sticky Intent that * matches <var>filter</var>. In the case of multiple matches, the same * rules as described above apply. * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * * <p class="note">Note: this method <em>cannot be called from a * {@link BroadcastReceiver} component;</em> that is, from a BroadcastReceiver * that is declared in an application's manifest. It is okay, however, to call * this method from another BroadcastReceiver that has itself been registered * at run time with {@link #registerReceiver}, since the lifetime of such a * registered BroadcastReceiver is tied to the object that registered it.</p>

* * @param receiver The BroadcastReceiver to handle the broadcast. * @param filter Selects the Intent broadcasts to be received. * * @return The first sticky intent found that matches <var>filter</var>, * or null if there are none. * * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) * @see #sendBroadcast * @see #unregisterReceiver */ public abstract Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter); /** * Register to receive intent broadcasts, to run in the context of * <var>scheduler</var>. See * {@link #registerReceiver(BroadcastReceiver, IntentFilter)} for more * information. This allows you to enforce permissions on who can * broadcast intents to your receiver, or have the receiver run in * a different thread than the main application thread. * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * * @param receiver The BroadcastReceiver to handle the broadcast. * @param filter Selects the Intent broadcasts to be received. * @param broadcastPermission String naming a permissions that a * broadcaster must hold in order to send an Intent to you. If null, * no permission is required. * @param scheduler Handler identifying the thread that will receive * the Intent. If null, the main thread of the process will be used. * * @return The first sticky intent found that matches <var>filter</var>, * or null if there are none. * * @see #registerReceiver(BroadcastReceiver, IntentFilter) * @see #sendBroadcast * @see #unregisterReceiver */ public abstract Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler); /** * Unregister a previously registered BroadcastReceiver. <em>All</em> * filters that have been registered for this BroadcastReceiver will be * removed. * * @param receiver The BroadcastReceiver to unregister. * * @see #registerReceiver */ public abstract void unregisterReceiver(BroadcastReceiver receiver); /** * Request that a given application service be started. The Intent * can either contain the complete class name of a specific service * implementation to start, or an abstract definition through the * action and other fields of the kind of service to start. If this service * is not already running, it will be instantiated and started (creating a * process for it if needed); if it is running then it remains running. * * <p>Every call to this method will result in a corresponding call to * the target service's {@link android.app.Service#onStart} method, * with the <var>intent</var> given here. This provides a convenient way * to submit jobs to a service without having to bind and call on to its * interface. * * <p>Using startService() overrides the default service lifetime that is * managed by {@link #bindService}: it requires the service to remain * running until {@link #stopService} is called, regardless of whether * any clients are connected to it. Note that calls to startService()

* are not nesting: no matter how many times you call startService(), * a single call to {@link #stopService} will stop it. * * <p>The system attempts to keep running services around as much as * possible. The only time they should be stopped is if the current * foreground application is using so many resources that the service needs * to be killed. If any errors happen in the service's process, it will * automatically be restarted. * * <p>This function will throw {@link SecurityException} if you do not * have permission to start the given service. * * @param service Identifies the service to be started. The Intent may * specify either an explicit component name to start, or a logical * description (action, category, etc) to match an * {@link IntentFilter} published by a service. Additional values * may be included in the Intent extras to supply arguments along with * this specific start call. * * @return If the service is being started or is already running, the * {@link ComponentName} of the actual service that was started is * returned; else if the service does not exist null is returned. * * @throws SecurityException * * @see #stopService * @see #bindService */ public abstract ComponentName startService(Intent service); /** * Request that a given application service be stopped. If the service is * not running, nothing happens. Otherwise it is stopped. Note that calls * to startService() are not counted -- this stops the service no matter * how many times it was started. * * <p>Note that if a stopped service still has {@link ServiceConnection} * objects bound to it with the {@link #BIND_AUTO_CREATE} set, it will * not be destroyed until all of these bindings are removed. See * the {@link android.app.Service} documentation for more details on a * service's lifecycle. * * <p>This function will throw {@link SecurityException} if you do not * have permission to stop the given service. * * @param service Description of the service to be stopped. The Intent may * specify either an explicit component name to start, or a logical * description (action, category, etc) to match an * {@link IntentFilter} published by a service. * * @return If there is a service matching the given Intent that is already * running, then it is stopped and true is returned; else false is returned. * * @throws SecurityException * * @see #startService */ public abstract boolean stopService(Intent service); /** * Connect to an application service, creating it if needed. This defines * a dependency between your application and the service. The given * <var>conn</var> will receive the service object when its created and be * told if it dies and restarts. The service will be considered required * by the system only for as long as the calling context exists. For * example, if this Context is an Activity that is stopped, the service will * not be required to continue running until the Activity is resumed. * * <p>This function will throw {@link SecurityException} if you do not * have permission to bind to the given service. * * <p class="note">Note: this method <em>can not be called from an * {@link BroadcastReceiver} component</em>. A pattern you can use to

* communicate from an BroadcastReceiver to a Service is to call * {@link #startService} with the arguments containing the command to be * sent, with the service calling its * {@link android.app.Service#stopSelf(int)} method when done executing * that command. See the API demo App/Service/Service Start Arguments * Controller for an illustration of this. It is okay, however, to use * this method from an BroadcastReceiver that has been registered with * {@link #registerReceiver}, since the lifetime of this BroadcastReceiver * is tied to another object (the one that registered it).</p> * * @param service Identifies the service to connect to. The Intent may * specify either an explicit component name, or a logical * description (action, category, etc) to match an * {@link IntentFilter} published by a service. * @param conn Receives information as the service is started and stopped. * @param flags Operation options for the binding. May be 0 or * {@link #BIND_AUTO_CREATE}. * @return If you have successfully bound to the service, true is returned; * false is returned if the connection is not made so you will not * receive the service object. * * @throws SecurityException * * @see #unbindService * @see #startService * @see #BIND_AUTO_CREATE */ public abstract boolean bindService(Intent service, ServiceConnection conn, int flags); /** * Disconnect from an application service. You will no longer receive * calls as the service is restarted, and the service is now allowed to * stop at any time. * * @param conn The connection interface previously supplied to * bindService(). * * @see #bindService */ public abstract void unbindService(ServiceConnection conn); /** * Start executing an {@link android.app.Instrumentation} class. The given * Instrumentation component will be run by killing its target application * (if currently running), starting the target process, instantiating the * instrumentation component, and then letting it drive the application. * * <p>This function is not synchronous -- it returns as soon as the * instrumentation has started and while it is running. * * <p>Instrumentation is normally only allowed to run against a package * that is either unsigned or signed with a signature that the * the instrumentation package is also signed with (ensuring the target * trusts the instrumentation). * * @param className Name of the Instrumentation component to be run. * @param profileFile Optional path to write profiling data as the * instrumentation runs, or null for no profiling. * @param arguments Additional optional arguments to pass to the * instrumentation, or null. * * @return Returns true if the instrumentation was successfully started, * else false if it could not be found. */ public abstract boolean startInstrumentation(ComponentName className, String profileFile, Bundle arguments); /** * Return the handle to a system-level service by name. The class of the * returned object varies by the requested name. Currently available names * are: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

<dl> <dt> {@link #WINDOW_SERVICE} ("window") <dd> The top-level window manager in which you can place custom windows. The returned object is a {@link android.view.WindowManager}. <dt> {@link #LAYOUT_INFLATER_SERVICE} ("layout_inflater") <dd> A {@link android.view.LayoutInflater} for inflating layout resources in this context. <dt> {@link #ACTIVITY_SERVICE} ("activity") <dd> A {@link android.app.ActivityManager} for interacting with the global activity state of the system. <dt> {@link #POWER_SERVICE} ("power") <dd> A {@link android.os.PowerManager} for controlling power management. <dt> {@link #ALARM_SERVICE} ("alarm") <dd> A {@link android.app.AlarmManager} for receiving intents at the time of your choosing. <dt> {@link #NOTIFICATION_SERVICE} ("notification") <dd> A {@link android.app.NotificationManager} for informing the user of background events. <dt> {@link #KEYGUARD_SERVICE} ("keyguard") <dd> A {@link android.app.KeyguardManager} for controlling keyguard. <dt> {@link #LOCATION_SERVICE} ("location") <dd> A {@link android.location.LocationManager} for controlling location (e.g., GPS) updates. <dt> {@link #SEARCH_SERVICE} ("search") <dd> A {@link android.app.SearchManager} for handling search. <dt> {@link #VIBRATOR_SERVICE} ("vibrator") <dd> A {@link android.os.Vibrator} for interacting with the vibrator hardware. <dt> {@link #CONNECTIVITY_SERVICE} ("connection") <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for handling management of network connections. <dt> {@link #WIFI_SERVICE} ("wifi") <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of Wi-Fi connectivity. <dt> {@link #INPUT_METHOD_SERVICE} ("input_method") <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager} for management of input methods. <dt> {@link #UI_MODE_SERVICE} ("uimode") <dd> An {@link android.app.UiModeManager} for controlling UI modes. </dl> <p>Note: System services obtained via this API may be closely associated with the Context in which they are obtained from. In general, do not share the service objects between various different contexts (Activities, Applications, Services, Providers, etc.) @param name The name of the desired service. @return The service or null if the name does not exist. @see @see @see @see @see @see @see @see @see @see @see @see @see @see @see @see @see @see @see @see @see @see #WINDOW_SERVICE android.view.WindowManager #LAYOUT_INFLATER_SERVICE android.view.LayoutInflater #ACTIVITY_SERVICE android.app.ActivityManager #POWER_SERVICE android.os.PowerManager #ALARM_SERVICE android.app.AlarmManager #NOTIFICATION_SERVICE android.app.NotificationManager #KEYGUARD_SERVICE android.app.KeyguardManager #LOCATION_SERVICE android.location.LocationManager #SEARCH_SERVICE android.app.SearchManager #SENSOR_SERVICE android.hardware.SensorManager #STORAGE_SERVICE android.os.storage.StorageManager

* @see #VIBRATOR_SERVICE * @see android.os.Vibrator * @see #CONNECTIVITY_SERVICE * @see android.net.ConnectivityManager * @see #WIFI_SERVICE * @see android.net.wifi.WifiManager * @see #AUDIO_SERVICE * @see android.media.AudioManager * @see #TELEPHONY_SERVICE * @see android.telephony.TelephonyManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager * @see #UI_MODE_SERVICE * @see android.app.UiModeManager */ public abstract Object getSystemService(String name); /** * Use with {@link #getSystemService} to retrieve a * {@link android.os.PowerManager} for controlling power management, * including "wake locks," which let you keep the device on while * you're running long tasks. */ public static final String POWER_SERVICE = "power"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.view.WindowManager} for accessing the system's window * manager. * * @see #getSystemService * @see android.view.WindowManager */ public static final String WINDOW_SERVICE = "window"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.view.LayoutInflater} for inflating layout resources in this * context. * * @see #getSystemService * @see android.view.LayoutInflater */ public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.accounts.AccountManager} for receiving intents at a * time of your choosing. * * @see #getSystemService * @see android.accounts.AccountManager */ public static final String ACCOUNT_SERVICE = "account"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.app.ActivityManager} for interacting with the global * system state. * * @see #getSystemService * @see android.app.ActivityManager */ public static final String ACTIVITY_SERVICE = "activity"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.app.AlarmManager} for receiving intents at a * time of your choosing. * * @see #getSystemService * @see android.app.AlarmManager */

public static final String ALARM_SERVICE = "alarm"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.app.NotificationManager} for informing the user of * background events. * * @see #getSystemService * @see android.app.NotificationManager */ public static final String NOTIFICATION_SERVICE = "notification"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.view.accessibility.AccessibilityManager} for giving the user * feedback for UI events through the registered event listeners. * * @see #getSystemService * @see android.view.accessibility.AccessibilityManager */ public static final String ACCESSIBILITY_SERVICE = "accessibility"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.app.NotificationManager} for controlling keyguard. * * @see #getSystemService * @see android.app.KeyguardManager */ public static final String KEYGUARD_SERVICE = "keyguard"; /** * Use with {@link #getSystemService} to retrieve a {@link * android.location.LocationManager} for controlling location * updates. * * @see #getSystemService * @see android.location.LocationManager */ public static final String LOCATION_SERVICE = "location"; /** * Use with {@link #getSystemService} to retrieve a {@link * android.app.SearchManager} for handling searches. * * @see #getSystemService * @see android.app.SearchManager */ public static final String SEARCH_SERVICE = "search"; /** * Use with {@link #getSystemService} to retrieve a {@link * android.hardware.SensorManager} for accessing sensors. * * @see #getSystemService * @see android.hardware.SensorManager */ public static final String SENSOR_SERVICE = "sensor"; /** * @hide * Use with {@link #getSystemService} to retrieve a {@link * android.os.storage.StorageManager} for accesssing system storage * functions. * * @see #getSystemService * @see android.os.storage.StorageManager */ public static final String STORAGE_SERVICE = "storage"; /** * Use with {@link #getSystemService} to retrieve a * com.android.server.WallpaperService for accessing wallpapers.

* * @see #getSystemService */ public static final String WALLPAPER_SERVICE = "wallpaper"; /** * Use with {@link #getSystemService} to retrieve a {@link * android.os.Vibrator} for interacting with the vibration hardware. * * @see #getSystemService * @see android.os.Vibrator */ public static final String VIBRATOR_SERVICE = "vibrator"; /** * Use with {@link #getSystemService} to retrieve a {@link * android.app.StatusBarManager} for interacting with the status bar. * * @see #getSystemService * @see android.app.StatusBarManager * @hide */ public static final String STATUS_BAR_SERVICE = "statusbar"; /** * Use with {@link #getSystemService} to retrieve a {@link * android.net.ConnectivityManager} for handling management of * network connections. * * @see #getSystemService * @see android.net.ConnectivityManager */ public static final String CONNECTIVITY_SERVICE = "connectivity"; /** * Use with {@link #getSystemService} to retrieve a {@link * android.net.ThrottleManager} for handling management of * throttling. * * @hide * @see #getSystemService * @see android.net.ThrottleManager */ public static final String THROTTLE_SERVICE = "throttle"; /** * Use with {@link #getSystemService} to retrieve a {@link * android.net.NetworkManagementService} for handling management of * system network services * * @hide * @see #getSystemService * @see android.net.NetworkManagementService */ public static final String NETWORKMANAGEMENT_SERVICE = "network_management"; /** * Use with {@link #getSystemService} to retrieve a {@link * android.net.wifi.WifiManager} for handling management of * Wi-Fi access. * * @see #getSystemService * @see android.net.wifi.WifiManager */ public static final String WIFI_SERVICE = "wifi"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.media.AudioManager} for handling management of volume, * ringer modes and audio routing. * * @see #getSystemService * @see android.media.AudioManager

*/ public static final String AUDIO_SERVICE = "audio"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.telephony.TelephonyManager} for handling management the * telephony features of the device. * * @see #getSystemService * @see android.telephony.TelephonyManager */ public static final String TELEPHONY_SERVICE = "phone"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.text.ClipboardManager} for accessing and modifying * the contents of the global clipboard. * * @see #getSystemService * @see android.text.ClipboardManager */ public static final String CLIPBOARD_SERVICE = "clipboard"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.view.inputmethod.InputMethodManager} for accessing input * methods. * * @see #getSystemService */ public static final String INPUT_METHOD_SERVICE = "input_method"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.appwidget.AppWidgetManager} for accessing AppWidgets. * * @hide * @see #getSystemService */ public static final String APPWIDGET_SERVICE = "appwidget"; /** * Use with {@link #getSystemService} to retrieve an * {@link android.app.backup.IBackupManager IBackupManager} for communicating * with the backup mechanism. * @hide * * @see #getSystemService */ public static final String BACKUP_SERVICE = "backup"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.os.DropBoxManager} instance for recording * diagnostic logs. * @see #getSystemService */ public static final String DROPBOX_SERVICE = "dropbox"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.app.admin.DevicePolicyManager} for working with global * device policy management. * * @see #getSystemService */ public static final String DEVICE_POLICY_SERVICE = "device_policy"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.app.UiModeManager} for controlling UI modes. * * @see #getSystemService

*/ public static final String UI_MODE_SERVICE = "uimode"; /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * * @param permission The name of the permission being checked. * @param pid The process ID being checked against. Must be > 0. * @param uid The user ID being checked against. A uid of 0 is the root * user, which will pass every permission check. * * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the given * pid/uid is allowed that permission, or * {@link PackageManager#PERMISSION_DENIED} if it is not. * * @see PackageManager#checkPermission(String, String) * @see #checkCallingPermission */ public abstract int checkPermission(String permission, int pid, int uid); /** * Determine whether the calling process of an IPC you are handling has been * granted a particular permission. This is basically the same as calling * {@link #checkPermission(String, int, int)} with the pid and uid returned * by {@link android.os.Binder#getCallingPid} and * {@link android.os.Binder#getCallingUid}. One important difference * is that if you are not currently processing an IPC, this function * will always fail. This is done to protect against accidentally * leaking permissions; you can use {@link #checkCallingOrSelfPermission} * to avoid this protection. * * @param permission The name of the permission being checked. * * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the calling * pid/uid is allowed that permission, or * {@link PackageManager#PERMISSION_DENIED} if it is not. * * @see PackageManager#checkPermission(String, String) * @see #checkPermission * @see #checkCallingOrSelfPermission */ public abstract int checkCallingPermission(String permission); /** * Determine whether the calling process of an IPC <em>or you</em> have been * granted a particular permission. This is the same as * {@link #checkCallingPermission}, except it grants your own permissions * if you are not currently processing an IPC. Use with care! * * @param permission The name of the permission being checked. * * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the calling * pid/uid is allowed that permission, or * {@link PackageManager#PERMISSION_DENIED} if it is not. * * @see PackageManager#checkPermission(String, String) * @see #checkPermission * @see #checkCallingPermission */ public abstract int checkCallingOrSelfPermission(String permission); /** * If the given permission is not allowed for a particular process * and user ID running in the system, throw a {@link SecurityException}. * * @param permission The name of the permission being checked. * @param pid The process ID being checked against. Must be &gt; 0. * @param uid The user ID being checked against. A uid of 0 is the root * user, which will pass every permission check. * @param message A message to include in the exception if it is thrown. * * @see #checkPermission(String, int, int)

*/ public abstract void enforcePermission( String permission, int pid, int uid, String message); /** * If the calling process of an IPC you are handling has not been * granted a particular permission, throw a {@link * SecurityException}. This is basically the same as calling * {@link #enforcePermission(String, int, int, String)} with the * pid and uid returned by {@link android.os.Binder#getCallingPid} * and {@link android.os.Binder#getCallingUid}. One important * difference is that if you are not currently processing an IPC, * this function will always throw the SecurityException. This is * done to protect against accidentally leaking permissions; you * can use {@link #enforceCallingOrSelfPermission} to avoid this * protection. * * @param permission The name of the permission being checked. * @param message A message to include in the exception if it is thrown. * * @see #checkCallingPermission(String) */ public abstract void enforceCallingPermission( String permission, String message); /** * If neither you nor the calling process of an IPC you are * handling has been granted a particular permission, throw a * {@link SecurityException}. This is the same as {@link * #enforceCallingPermission}, except it grants your own * permissions if you are not currently processing an IPC. Use * with care! * * @param permission The name of the permission being checked. * @param message A message to include in the exception if it is thrown. * * @see #checkCallingOrSelfPermission(String) */ public abstract void enforceCallingOrSelfPermission( String permission, String message); /** * Grant permission to access a specific Uri to another package, regardless * of whether that package has general permission to access the Uri's * content provider. This can be used to grant specific, temporary * permissions, typically in response to user interaction (such as the * user opening an attachment that you would like someone else to * display). * * <p>Normally you should use {@link Intent#FLAG_GRANT_READ_URI_PERMISSION * Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} with the Intent being used to * start an activity instead of this function directly. If you use this * function directly, you should be sure to call * {@link #revokeUriPermission} when the target should no longer be allowed * to access it. * * <p>To succeed, the content provider owning the Uri must have set the * {@link android.R.styleable#AndroidManifestProvider_grantUriPermissions * grantUriPermissions} attribute in its manifest or included the * {@link android.R.styleable#AndroidManifestGrantUriPermission * &lt;grant-uri-permissions&gt;} tag. * * @param toPackage The package you would like to allow to access the Uri. * @param uri The Uri you would like to grant access to. * @param modeFlags The desired access modes. Any combination of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION * Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION * Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. * * @see #revokeUriPermission

*/ public abstract void grantUriPermission(String toPackage, Uri uri, int modeFlags); /** * Remove all permissions to access a particular content provider Uri * that were previously added with {@link #grantUriPermission}. The given * Uri will match all previously granted Uris that are the same or a * sub-path of the given Uri. That is, revoking "content://foo/one" will * revoke both "content://foo/target" and "content://foo/target/sub", but not * "content://foo". * * @param uri The Uri you would like to revoke access to. * @param modeFlags The desired access modes. Any combination of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION * Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION * Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. * * @see #grantUriPermission */ public abstract void revokeUriPermission(Uri uri, int modeFlags); /** * Determine whether a particular process and user ID has been granted * permission to access a specific URI. This only checks for permissions * that have been explicitly granted -- if the given process/uid has * more general access to the URI's content provider then this check will * always fail. * * @param uri The uri that is being checked. * @param pid The process ID being checked against. Must be &gt; 0. * @param uid The user ID being checked against. A uid of 0 is the root * user, which will pass every permission check. * @param modeFlags The type of access to grant. May be one or both of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. * * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the given * pid/uid is allowed to access that uri, or * {@link PackageManager#PERMISSION_DENIED} if it is not. * * @see #checkCallingUriPermission */ public abstract int checkUriPermission(Uri uri, int pid, int uid, int modeFlags); /** * Determine whether the calling process and user ID has been * granted permission to access a specific URI. This is basically * the same as calling {@link #checkUriPermission(Uri, int, int, * int)} with the pid and uid returned by {@link * android.os.Binder#getCallingPid} and {@link * android.os.Binder#getCallingUid}. One important difference is * that if you are not currently processing an IPC, this function * will always fail. * * @param uri The uri that is being checked. * @param modeFlags The type of access to grant. May be one or both of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. * * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the caller * is allowed to access that uri, or * {@link PackageManager#PERMISSION_DENIED} if it is not. * * @see #checkUriPermission(Uri, int, int, int) */ public abstract int checkCallingUriPermission(Uri uri, int modeFlags); /** * Determine whether the calling process of an IPC <em>or you</em> has been granted * permission to access a specific URI. This is the same as * {@link #checkCallingUriPermission}, except it grants your own permissions

* if you are not currently processing an IPC. Use with care! * * @param uri The uri that is being checked. * @param modeFlags The type of access to grant. May be one or both of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. * * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the caller * is allowed to access that uri, or * {@link PackageManager#PERMISSION_DENIED} if it is not. * * @see #checkCallingUriPermission */ public abstract int checkCallingOrSelfUriPermission(Uri uri, int modeFlags); /** * Check both a Uri and normal permission. This allows you to perform * both {@link #checkPermission} and {@link #checkUriPermission} in one * call. * * @param uri The Uri whose permission is to be checked, or null to not * do this check. * @param readPermission The permission that provides overall read access, * or null to not do this check. * @param writePermission The permission that provides overall write * acess, or null to not do this check. * @param pid The process ID being checked against. Must be &gt; 0. * @param uid The user ID being checked against. A uid of 0 is the root * user, which will pass every permission check. * @param modeFlags The type of access to grant. May be one or both of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. * * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the caller * is allowed to access that uri or holds one of the given permissions, or * {@link PackageManager#PERMISSION_DENIED} if it is not. */ public abstract int checkUriPermission(Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags); /** * If a particular process and user ID has not been granted * permission to access a specific URI, throw {@link * SecurityException}. This only checks for permissions that have * been explicitly granted -- if the given process/uid has more * general access to the URI's content provider then this check * will always fail. * * @param uri The uri that is being checked. * @param pid The process ID being checked against. Must be &gt; 0. * @param uid The user ID being checked against. A uid of 0 is the root * user, which will pass every permission check. * @param modeFlags The type of access to grant. May be one or both of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. * @param message A message to include in the exception if it is thrown. * * @see #checkUriPermission(Uri, int, int, int) */ public abstract void enforceUriPermission( Uri uri, int pid, int uid, int modeFlags, String message); /** * If the calling process and user ID has not been granted * permission to access a specific URI, throw {@link * SecurityException}. This is basically the same as calling * {@link #enforceUriPermission(Uri, int, int, int, String)} with * the pid and uid returned by {@link * android.os.Binder#getCallingPid} and {@link * android.os.Binder#getCallingUid}. One important difference is * that if you are not currently processing an IPC, this function * will always throw a SecurityException. *

* @param uri The uri that is being checked. * @param modeFlags The type of access to grant. May be one or both of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. * @param message A message to include in the exception if it is thrown. * * @see #checkCallingUriPermission(Uri, int) */ public abstract void enforceCallingUriPermission( Uri uri, int modeFlags, String message); /** * If the calling process of an IPC <em>or you</em> has not been * granted permission to access a specific URI, throw {@link * SecurityException}. This is the same as {@link * #enforceCallingUriPermission}, except it grants your own * permissions if you are not currently processing an IPC. Use * with care! * * @param uri The uri that is being checked. * @param modeFlags The type of access to grant. May be one or both of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. * @param message A message to include in the exception if it is thrown. * * @see #checkCallingOrSelfUriPermission(Uri, int) */ public abstract void enforceCallingOrSelfUriPermission( Uri uri, int modeFlags, String message); /** * Enforce both a Uri and normal permission. This allows you to perform * both {@link #enforcePermission} and {@link #enforceUriPermission} in one * call. * * @param uri The Uri whose permission is to be checked, or null to not * do this check. * @param readPermission The permission that provides overall read access, * or null to not do this check. * @param writePermission The permission that provides overall write * acess, or null to not do this check. * @param pid The process ID being checked against. Must be &gt; 0. * @param uid The user ID being checked against. A uid of 0 is the root * user, which will pass every permission check. * @param modeFlags The type of access to grant. May be one or both of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. * @param message A message to include in the exception if it is thrown. * * @see #checkUriPermission(Uri, String, String, int, int, int) */ public abstract void enforceUriPermission( Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags, String message); /** * Flag for use with {@link #createPackageContext}: include the application * code with the context. This means loading code into the caller's * process, so that {@link #getClassLoader()} can be used to instantiate * the application's classes. Setting this flags imposes security * restrictions on what application context you can access; if the * requested application can not be safely loaded into your process, * java.lang.SecurityException will be thrown. If this flag is not set, * there will be no restrictions on the packages that can be loaded, * but {@link #getClassLoader} will always return the default system * class loader. */ public static final int CONTEXT_INCLUDE_CODE = 0x00000001; /** * Flag for use with {@link #createPackageContext}: ignore any security * restrictions on the Context being requested, allowing it to always * be loaded. For use with {@link #CONTEXT_INCLUDE_CODE} to allow code

* to be loaded into a process even when it isn't safe to do so. * with extreme care! */ public static final int CONTEXT_IGNORE_SECURITY = 0x00000002;

Use

/** * Flag for use with {@link #createPackageContext}: a restricted context may * disable specific features. For instance, a View associated with a restricted * context would ignore particular XML attributes. */ public static final int CONTEXT_RESTRICTED = 0x00000004; /** * Return a new Context object for the given application name. This * Context is the same as what the named application gets when it is * launched, containing the same resources and class loader. Each call to * this method returns a new instance of a Context object; Context objects * are not shared, however they share common state (Resources, ClassLoader, * etc) so the Context instance itself is fairly lightweight. * * <p>Throws {@link PackageManager.NameNotFoundException} if there is no * application with the given package name. * * <p>Throws {@link java.lang.SecurityException} if the Context requested * can not be loaded into the caller's process for security reasons (see * {@link #CONTEXT_INCLUDE_CODE} for more information}. * * @param packageName Name of the application's package. * @param flags Option flags, one of {@link #CONTEXT_INCLUDE_CODE} * or {@link #CONTEXT_IGNORE_SECURITY}. * * @return A Context for the application. * * @throws java.lang.SecurityException * @throws PackageManager.NameNotFoundException if there is no application with * the given package name */ public abstract Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException; /** * Indicates whether this Context is restricted. * * @return True if this Context is restricted, false otherwise. * * @see #CONTEXT_RESTRICTED */ public boolean isRestricted() { return false; } }

ATTACHMENT E

public class

Intent
extends Object implements Parcelable Cloneable
java.lang.Object android.content.Intent Known Direct Subclasses LabeledIntent

Class Overview
An intent is an abstract description of an operation to be performed. It can be used with startActivity to launch an Activity, broadcastIntent to send it to any interested BroadcastReceiver components, and startService(Intent) or bindService(Intent, ServiceConnection, int) to communicate with a background Service. An Intent provides a facility for performing late runtime binding between the code in different applications. Its most significant use is in the launching of activities, where it can be thought of as the glue between activities. It is basically a passive data structure holding an abstract description of an action to be performed. The primary pieces of information in an intent are:

action -- The general action to be performed, such as ACTION_VIEW, ACTION_EDIT, ACTION_MAIN, etc. data -- The data to operate on, such as a person record in the contacts database, expressed as a Uri.

Some examples of action/data pairs are:


ACTION_VIEW content://contacts/people/1 -- Display information about the person whose identifier is "1". ACTION_DIAL content://contacts/people/1 -- Display the phone dialer with the person filled in. ACTION_VIEW tel:123 -- Display the phone dialer with the given number filled in. Note how the VIEW action does what what is considered the most reasonable thing for a particular URI. ACTION_DIAL tel:123 -- Display the phone dialer with the given number filled in. ACTION_EDIT content://contacts/people/1 -- Edit information about the person whose identifier is "1". ACTION_VIEW content://contacts/people/ -- Display a list of people, which the user can browse through. This example is a typical top-level entry into the Contacts application, showing you the list of people. Selecting a particular person to view would result in a new intent { ACTION_VIEW content://contacts/N } being used to start an activity to display that person.

In addition to these primary attributes, there are a number of secondary attributes that you can also include with an intent:

category -- Gives additional information about the action to execute. For example, CATEGORY_LAUNCHER means it should appear in the Launcher as a top-level application, while CATEGORY_ALTERNATIVE means it should be included in a list of alternative actions the user can perform on a piece of data. type -- Specifies an explicit type (a MIME type) of the intent data. Normally the type is inferred from the data itself. By setting this attribute, you disable that evaluation and force an explicit type. component -- Specifies an explicit name of a component class to use for the intent. Normally this is determined by looking at the other information in the intent (the action, data/type, and categories) and matching that with a component that can handle it. If this attribute is set then none of the evaluation is performed, and this component is used exactly as is. By specifying this attribute, all of the other Intent attributes become optional. extras -- This is a Bundle of any additional information. This can be used to provide extended information to the component.

For example, if we have a action to send an e-mail message, we could also include extra pieces of data here to supply a subject, body, etc. Here are some examples of other operations you can specify as intents using these additional parameters:

ACTION_MAIN with category CATEGORY_HOME -- Launch the home screen. ACTION_GET_CONTENT with MIME type vnd.android.cursor.item/phone -- Display the list of people's phone numbers, allowing the user to browse through them and pick one and return it to the parent activity. ACTION_GET_CONTENT with MIME type */* and category CATEGORY_OPENABLE -- Display all pickers for data that can be opened with ContentResolver.openInputStream(), allowing the user to pick one of them and then some data inside of it and returning the resulting URI to the caller. This can be used, for example, in an e-mail application to allow the user to pick some data to include as an attachment.

There are a variety of standard Intent action and category constants defined in the Intent class, but applications can also define their own. These strings use java style scoping, to ensure they are unique -- for example, the standard ACTION_VIEW is called "android.intent.action.VIEW". Put together, the set of actions, data types, categories, and extra data defines a language for the system allowing for the expression of phrases such as "call john smith's cell". As applications are added to the system, they can extend this language by adding new actions, types, and categories, or they can modify the behavior of existing phrases by supplying their own activities that handle them.

Intent Resolution
There are two primary forms of intents you will use.

Explicit Intents have specified a component (via setComponent(ComponentName) or setClass(Context, Class)), which provides the exact class to be run. Often these will not include any other information, simply being a way for an application to launch various internal activities it has as the user interacts with the application. Implicit Intents have not specified a component; instead, they must include enough information for the system to determine which of the available components is best to run for that intent.

When using implicit intents, given such an arbitrary intent we need to know what to do with it. This is handled by the process of Intent resolution, which maps an Intent to an Activity, BroadcastReceiver, or Service (or sometimes two or more activities/receivers) that can handle it. The intent resolution mechanism basically revolves around matching an Intent against all of the <intent-filter> descriptions in the installed application packages. (Plus, in the case of broadcasts, any BroadcastReceiver objects explicitly registered with registerReceiver(BroadcastReceiver, IntentFilter).) More details on this can be found in the documentation on the IntentFilter class. There are three pieces of information in the Intent that are used for resolution: the action, type, and category. Using this information, a query is done on the PackageManager for a component that can handle the intent. The appropriate component is determined based on the intent information supplied in the AndroidManifest.xml file as follows:

The action, if given, must be listed by the component as one it handles. The type is retrieved from the Intent's data, if not already supplied in the Intent. Like the action, if a type is included in the intent (either explicitly or implicitly in its data), then this must be listed by the component as one it handles. For data that is not a content: URI and where no explicit type is included in the Intent, instead the scheme of the intent data (such as http: or mailto:) is considered. Again like the action, if we are matching a scheme it must be listed by the component as one it can handle. The categories, if supplied, must all be listed by the activity as categories it handles. That is, if you include the categories CATEGORY_LAUNCHER and CATEGORY_ALTERNATIVE, then you will only resolve to components with an intent that lists both of those categories. Activities will very often need to support the CATEGORY_DEFAULT so that they can be found by Context.startActivity().

For example, consider the Note Pad sample application that allows user to browse through a list of notes data and view details

about individual items. Text in italics indicate places were you would replace a name with one specific to your own package. <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.notepad"> <application android:icon="@drawable/app_notes" android:label="@string/app_name"> <provider class=".NotePadProvider" android:authorities="com.google.provider.NotePad" /> <activity class=".NotesList" android:label="@string/title_notes_list"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.PICK" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.GET_CONTENT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter> </activity> <activity class=".NoteEditor" android:label="@string/title_note"> <intent-filter android:label="@string/resolve_edit"> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.EDIT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.INSERT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> </intent-filter> </activity> <activity class=".TitleEditor" android:label="@string/title_edit_title" android:theme="@android:style/Theme.Dialog"> <intent-filter android:label="@string/resolve_title"> <action android:name="com.android.notepad.action.EDIT_TITLE" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.ALTERNATIVE" /> <category android:name="android.intent.category.SELECTED_ALTERNATIVE" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter> </activity> </application> </manifest> The first activity, com.android.notepad.NotesList, serves as our main entry into the app. It can do three things as described by its three intent templates: 1. <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter>

This provides a top-level entry into the NotePad application: the standard MAIN action is a main entry point (not requiring any other information in the Intent), and the LAUNCHER category says that this entry point should be listed in the application launcher. 2. <intent-filter> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.PICK" /> <category android:name="android.intent.category.DEFAULT" /> <data mimeType:name="vnd.android.cursor.dir/vnd.google.note" /> </intent-filter>

This declares the things that the activity can do on a directory of notes. The type being supported is given with the <type> tag, where vnd.android.cursor.dir/vnd.google.note is a URI from which a Cursor of zero or more items (vnd.android.cursor.dir) can be retrieved which holds our note pad data (vnd.google.note). The activity allows the user to view or edit the directory of data (via the VIEW and EDIT actions), or to pick a particular note and return it to the caller (via the PICK action). Note also the DEFAULT category supplied here: this is required for the Context.startActivity method to resolve your activity when its component name is not explicitly specified. 3. <intent-filter> <action android:name="android.intent.action.GET_CONTENT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter>

This filter describes the ability return to the caller a note selected by the user without needing to know where it came from. The data type vnd.android.cursor.item/vnd.google.note is a URI from which a Cursor of exactly one (vnd.android.cursor.item) item can be retrieved which contains our note pad data (vnd.google.note). The GET_CONTENT action is similar to the PICK action, where the activity will return to its caller a piece of data selected by the user. Here, however, the caller specifies the type of data they desire instead of the type of data the user will be picking from. Given these capabilities, the following intents will resolve to the NotesList activity:

{ action=android.app.action.MAIN } matches all of the activities that can be used as top-level entry points into an application. { action=android.app.action.MAIN, category=android.app.category.LAUNCHER } is the actual intent used by the Launcher to populate its top-level list. { action=android.intent.action.VIEW data=content://com.google.provider.NotePad/notes } displays a list of all the notes under "content://com.google.provider.NotePad/notes", which the user can browse through and see the details on. { action=android.app.action.PICK data=content://com.google.provider.NotePad/notes } provides a list of the notes under "content://com.google.provider.NotePad/notes", from which the user can pick a note whose data URL is returned back to the caller. { action=android.app.action.GET_CONTENT type=vnd.android.cursor.item/vnd.google.note } is similar to the pick action, but allows the caller to specify the kind of data they want back so that the system can find the appropriate activity to pick something of that data type.

The second activity, com.android.notepad.NoteEditor, shows the user a single note entry and allows them to edit it. It can do two things as described by its two intent templates: 1. <intent-filter android:label="@string/resolve_edit"> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.EDIT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter>

The first, primary, purpose of this activity is to let the user interact with a single note, as decribed by the MIME type vnd.android.cursor.item/vnd.google.note. The activity can either VIEW a note or allow the user to EDIT it. Again we support the DEFAULT category to allow the activity to be launched without explicitly specifying its component. 2. <intent-filter> <action android:name="android.intent.action.INSERT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> </intent-filter>

The secondary use of this activity is to insert a new note entry into an existing directory of notes. This is used when the user creates a new note: the INSERT action is executed on the directory of notes, causing this activity to run and have the user create the new note data which it then adds to the content provider. Given these capabilities, the following intents will resolve to the NoteEditor activity:

{ action=android.intent.action.VIEW data=content://com.google.provider.NotePad/notes/{ID} } shows the user the content of note {ID}. { action=android.app.action.EDIT data=content://com.google.provider.NotePad/notes/{ID} } allows the user to edit the content of note {ID}. { action=android.app.action.INSERT data=content://com.google.provider.NotePad/notes } creates a new, empty note in the notes list at "content://com.google.provider.NotePad/notes" and allows the user to edit it. If they keep their changes, the URI of the newly created note is returned to the caller.

The last activity, com.android.notepad.TitleEditor, allows the user to edit the title of a note. This could be implemented as a class that the application directly invokes (by explicitly setting its component in the Intent), but here we show a way you can publish alternative operations on existing data: <intent-filter android:label="@string/resolve_title"> <action android:name="com.android.notepad.action.EDIT_TITLE" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.ALTERNATIVE" /> <category android:name="android.intent.category.SELECTED_ALTERNATIVE" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter> In the single intent template here, we have created our own private action called com.android.notepad.action.EDIT_TITLE which means to edit the title of a note. It must be invoked on a specific note (data type vnd.android.cursor.item/vnd.google.note) like the previous view and edit actions, but here displays and edits the title contained in the note data. In addition to supporting the default category as usual, our title editor also supports two other standard categories: ALTERNATIVE and SELECTED_ALTERNATIVE. Implementing these categories allows others to find the special action it provides without directly knowing about it, through the queryIntentActivityOptions(ComponentName, Intent[], Intent, int) method, or more often to build dynamic menu items with addIntentOptions(int, int, int, ComponentName, Intent[], Intent, int, MenuItem[]). Note that in the intent template here was also supply an explicit name for the template (via android:label="@string/resolve_title") to better control what the user sees when presented with this activity as an alternative action to the data they are viewing. Given these capabilities, the following intent will resolve to the TitleEditor activity:

{ action=com.android.notepad.action.EDIT_TITLE data=content://com.google.provider.NotePad/notes/{ID} } displays and allows the user to edit the title associated with note {ID}.

Standard Activity Actions


These are the current standard actions that Intent defines for launching activities (usually through startActivity(Intent). The most important, and by far most frequently used, are ACTION_MAIN and ACTION_EDIT.

ACTION_MAIN ACTION_VIEW ACTION_ATTACH_DATA ACTION_EDIT ACTION_PICK ACTION_CHOOSER ACTION_GET_CONTENT ACTION_DIAL ACTION_CALL ACTION_SEND ACTION_SENDTO ACTION_ANSWER

ACTION_INSERT ACTION_DELETE ACTION_RUN ACTION_SYNC ACTION_PICK_ACTIVITY ACTION_SEARCH ACTION_WEB_SEARCH ACTION_FACTORY_TEST

Standard Broadcast Actions


These are the current standard actions that Intent defines for receiving broadcasts (usually through registerReceiver (BroadcastReceiver, IntentFilter) or a <receiver> tag in a manifest).

ACTION_TIME_TICK ACTION_TIME_CHANGED ACTION_TIMEZONE_CHANGED ACTION_BOOT_COMPLETED ACTION_PACKAGE_ADDED ACTION_PACKAGE_CHANGED ACTION_PACKAGE_REMOVED ACTION_PACKAGE_RESTARTED ACTION_PACKAGE_DATA_CLEARED ACTION_UID_REMOVED ACTION_BATTERY_CHANGED ACTION_POWER_CONNECTED ACTION_POWER_DISCONNECTED ACTION_SHUTDOWN

Standard Categories
These are the current standard categories that can be used to further clarify an Intent via addCategory(String).

CATEGORY_DEFAULT CATEGORY_BROWSABLE CATEGORY_TAB CATEGORY_ALTERNATIVE CATEGORY_SELECTED_ALTERNATIVE CATEGORY_LAUNCHER CATEGORY_INFO CATEGORY_HOME CATEGORY_PREFERENCE CATEGORY_TEST CATEGORY_CAR_DOCK CATEGORY_DESK_DOCK CATEGORY_CAR_MODE

Standard Extra Data


These are the current standard fields that can be used as extra data via putExtra(String, Bundle).

EXTRA_ALARM_COUNT EXTRA_BCC EXTRA_CC EXTRA_CHANGED_COMPONENT_NAME EXTRA_DATA_REMOVED EXTRA_DOCK_STATE EXTRA_DOCK_STATE_CAR EXTRA_DOCK_STATE_DESK EXTRA_DOCK_STATE_UNDOCKED EXTRA_DONT_KILL_APP EXTRA_EMAIL EXTRA_INITIAL_INTENTS EXTRA_INTENT EXTRA_KEY_EVENT EXTRA_PHONE_NUMBER EXTRA_REMOTE_INTENT_TOKEN EXTRA_REPLACING EXTRA_SHORTCUT_ICON EXTRA_SHORTCUT_ICON_RESOURCE EXTRA_SHORTCUT_INTENT EXTRA_STREAM EXTRA_SHORTCUT_NAME EXTRA_SUBJECT EXTRA_TEMPLATE EXTRA_TEXT EXTRA_TITLE EXTRA_UID

Flags
These are the possible flags that can be used in the Intent via setFlags(int) and addFlags(int). See setFlags(int) for a list of all possible flags.

Summary
Nested Classes class class Intent.FilterComparison Intent.ShortcutIconResource Wrapper class holding an Intent and implementing comparisons on it for the purpose of filtering. Represents a shortcut/live folder icon resource.

Constants String ACTION_AIRPLANE_MODE_CHANGED Broadcast Action: The user has switched the phone

into or out of Airplane Mode. String ACTION_ALL_APPS Activity Action: List all available applications Input: Nothing. Activity Action: Handle an incoming phone call. Used to indicate that some piece of data should be attached to some other place. Broadcast Action: This is a sticky broadcast containing the charging state, level, and other information about the battery. Broadcast Action: Indicates low battery condition on the device. Broadcast Action: Indicates the battery is now okay after being low. Broadcast Action: This is broadcast once, after the system has finished booting. Activity Action: Show activity for reporting a bug. Activity Action: Perform a call to someone specified by the data. Activity Action: The user pressed the "call" button to go to the dialer or other appropriate UI for placing a call. Broadcast Action: The "Camera Button" was pressed. Activity Action: Display an activity chooser, allowing the user to pick what they want to before proceeding. Broadcast Action: This is broadcast when a user action should request a temporary system dialog to dismiss. Broadcast Action: The current device Configuration (orientation, locale, etc) has changed. Activity Action: Creates a shortcut. Broadcast Action: The date has changed. A synonym for ACTION_VIEW, the "standard" action that is performed on a piece of data. Activity Action: Delete the given data from its container. Broadcast Action: A sticky broadcast that indicates low memory condition on the device This is a protected intent that can only be sent by the system. String ACTION_DEVICE_STORAGE_OK Broadcast Action: Indicates low memory condition on the device no longer exists This is a protected intent that can only be sent by the system. String String String ACTION_DIAL ACTION_DOCK_EVENT ACTION_EDIT Activity Action: Dial a number as specified by the data. Broadcast Action: A sticky broadcast for changes in the physical docking state of the device. Activity Action: Provide explicit editable access to the given data.

String String String

ACTION_ANSWER ACTION_ATTACH_DATA ACTION_BATTERY_CHANGED

String String String String String String

ACTION_BATTERY_LOW ACTION_BATTERY_OKAY ACTION_BOOT_COMPLETED ACTION_BUG_REPORT ACTION_CALL ACTION_CALL_BUTTON

String String String

ACTION_CAMERA_BUTTON ACTION_CHOOSER ACTION_CLOSE_SYSTEM_DIALOGS

String

ACTION_CONFIGURATION_CHANGED

String String String String String

ACTION_CREATE_SHORTCUT ACTION_DATE_CHANGED ACTION_DEFAULT ACTION_DELETE ACTION_DEVICE_STORAGE_LOW

String

ACTION_EXTERNAL_APPLICATIONS_AVAILABLE

Broadcast Action: Resources for a set of packages (which were previously unavailable) are currently available since the media on which they exist is available. Broadcast Action: Resources for a set of packages are currently unavailable since the media on which they exist is unavailable. Activity Action: Main entry point for factory tests. Activity Action: Allow the user to select a particular kind of data and return it. Broadcast Action: An GTalk connection has been established. Broadcast Action: An GTalk connection has been disconnected. Broadcast Action: Wired Headset plugged in or unplugged. Broadcast Action: An input method has been changed. Activity Action: Insert an empty item into the given container. Activity Action: Pick an existing item, or insert a new item, and then edit it. Broadcast Action: The current device's locale has changed. Activity Action: Start as a main entry point, does not expect to receive data. Broadcast Action: Indicates low memory condition notification acknowledged by user and package management should be started. Broadcast Action: External media was removed from SD card slot, but mount point was not unmounted. Broadcast Action: The "Media Button" was pressed. Broadcast Action: External media is present, and being disk-checked The path to the mount point for the checking media is contained in the Intent.mData field. Broadcast Action: User has expressed the desire to remove the external storage media. Broadcast Action: External media is present and mounted at its mount point. Broadcast Action: External media is present, but is using an incompatible fs (or is blank) The path to the mount point for the checking media is contained in the Intent.mData field. Broadcast Action: External media has been removed. Broadcast Action: The media scanner has finished scanning a directory. Broadcast Action: Request the media scanner to scan a file and add it to the media database. Broadcast Action: The media scanner has started scanning a directory. Broadcast Action: External media is unmounted because it is being shared via USB mass storage.

String

ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE

String String String String String String String String String String String

ACTION_FACTORY_TEST ACTION_GET_CONTENT ACTION_GTALK_SERVICE_CONNECTED ACTION_GTALK_SERVICE_DISCONNECTED ACTION_HEADSET_PLUG ACTION_INPUT_METHOD_CHANGED ACTION_INSERT ACTION_INSERT_OR_EDIT ACTION_LOCALE_CHANGED ACTION_MAIN ACTION_MANAGE_PACKAGE_STORAGE

String String String

ACTION_MEDIA_BAD_REMOVAL ACTION_MEDIA_BUTTON ACTION_MEDIA_CHECKING

String String String

ACTION_MEDIA_EJECT ACTION_MEDIA_MOUNTED ACTION_MEDIA_NOFS

String String String String String

ACTION_MEDIA_REMOVED ACTION_MEDIA_SCANNER_FINISHED ACTION_MEDIA_SCANNER_SCAN_FILE ACTION_MEDIA_SCANNER_STARTED ACTION_MEDIA_SHARED

String String String String String String String String String

ACTION_MEDIA_UNMOUNTABLE ACTION_MEDIA_UNMOUNTED ACTION_NEW_OUTGOING_CALL ACTION_PACKAGE_ADDED ACTION_PACKAGE_CHANGED ACTION_PACKAGE_DATA_CLEARED ACTION_PACKAGE_INSTALL ACTION_PACKAGE_REMOVED ACTION_PACKAGE_REPLACED

Broadcast Action: External media is present but cannot be mounted. Broadcast Action: External media is present, but not mounted at its mount point. Broadcast Action: An outgoing call is about to be placed. Broadcast Action: A new application package has been installed on the device. Broadcast Action: An existing application package has been changed (e.g. Broadcast Action: The user has cleared the data of a package. Broadcast Action: Trigger the download and eventual installation of a package. Broadcast Action: An existing application package has been removed from the device. Broadcast Action: A new version of an application package has been installed, replacing an existing version that was previously installed. Broadcast Action: The user has restarted a package, and all of its processes have been killed. Activity Action: Pick an item from the data, returning what was selected. Activity Action: Pick an activity given an intent, returning the class selected. Broadcast Action: External power has been connected to the device. Broadcast Action: External power has been removed from the device. Activity Action: Show power usage information to the user. Broadcast Action: Some content providers have parts of their namespace where they publish new events or items that the user may be especially interested in. Broadcast Action: Have the device reboot. Activity Action: Run the data, whatever that means. Broadcast Action: Sent after the screen turns off. Broadcast Action: Sent after the screen turns on. Activity Action: Perform a search. Activity Action: Start action associated with long pressing on the search key. Activity Action: Deliver some data to someone else. Activity Action: Send a message to someone specified by the data. Activity Action: Deliver multiple data to someone else. Activity Action: Show settings for choosing wallpaper Input: Nothing. Broadcast Action: Device is shutting down. Activity Action: Perform a data synchronization.

String String String String String String String

ACTION_PACKAGE_RESTARTED ACTION_PICK ACTION_PICK_ACTIVITY ACTION_POWER_CONNECTED ACTION_POWER_DISCONNECTED ACTION_POWER_USAGE_SUMMARY ACTION_PROVIDER_CHANGED

String String String String String String String String String String

ACTION_REBOOT ACTION_RUN ACTION_SCREEN_OFF ACTION_SCREEN_ON ACTION_SEARCH ACTION_SEARCH_LONG_PRESS ACTION_SEND ACTION_SENDTO ACTION_SEND_MULTIPLE ACTION_SET_WALLPAPER

String String

ACTION_SHUTDOWN ACTION_SYNC

String

ACTION_SYSTEM_TUTORIAL

Activity Action: Start the platform-defined tutorial Input: getStringExtra(SearchManager.QUERY) is the text to search for. Broadcast Action: The timezone has changed. Broadcast Action: The time was set. Broadcast Action: The current time has changed. Broadcast Action: A user ID has been removed from the system. Broadcast Action: The device has entered USB Mass Storage mode. Broadcast Action: The device has exited USB Mass Storage mode. Broadcast Action: Sent when the user is present after device wakes up (e.g when the keyguard is gone). Activity Action: Display the data to the user. Activity Action: Start Voice Command. Broadcast Action: The current system wallpaper has changed. Activity Action: Perform a web search. Set if the activity should be considered as an alternative action to the data the user is currently viewing. Activities that can be safely invoked from a browser must support this category. An activity to run when device is inserted into a car dock. Used to indicate that the activity can be used in a car environment. Set if the activity should be an option for the default action (center press) to perform on a piece of data. An activity to run when device is inserted into a car dock. This activity is a development preference panel. Capable of running inside a parent activity container. To be used as code under test for framework instrumentation tests. This is the home activity, that is the first activity that is displayed when the device boots. Provides information about the package it is in; typically used if a package does not contain a CATEGORY_LAUNCHER to provide a front-door to the user without having to be shown in the all apps list. Should be displayed in the top-level launcher. This activity may be exercised by the monkey or other automated test tools. Used to indicate that a GET_CONTENT intent only wants URIs that can be opened with ContentResolver.openInputStream. This activity is a preference panel. To be used as an sample code example (not part of

String String String String String String String String String String String String

ACTION_TIMEZONE_CHANGED ACTION_TIME_CHANGED ACTION_TIME_TICK ACTION_UID_REMOVED ACTION_UMS_CONNECTED ACTION_UMS_DISCONNECTED ACTION_USER_PRESENT ACTION_VIEW ACTION_VOICE_COMMAND ACTION_WALLPAPER_CHANGED ACTION_WEB_SEARCH CATEGORY_ALTERNATIVE

String String String String String String String String String String

CATEGORY_BROWSABLE CATEGORY_CAR_DOCK CATEGORY_CAR_MODE CATEGORY_DEFAULT CATEGORY_DESK_DOCK CATEGORY_DEVELOPMENT_PREFERENCE CATEGORY_EMBED CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST CATEGORY_HOME CATEGORY_INFO

String String String

CATEGORY_LAUNCHER CATEGORY_MONKEY CATEGORY_OPENABLE

String String

CATEGORY_PREFERENCE CATEGORY_SAMPLE_CODE

the normal user experience). String CATEGORY_SELECTED_ALTERNATIVE Set if the activity should be considered as an alternative selection action to the data the user has currently selected. Intended to be used as a tab inside of an containing TabActivity. To be used as a test (not part of the normal user experience). To be used as a unit test (run through the Test Harness).

String String String Creator<Intent> String

CATEGORY_TAB CATEGORY_TEST CATEGORY_UNIT_TEST CREATOR EXTRA_ALARM_COUNT

Used as an int extra field in AlarmManager intents to tell the application being invoked how many pending alarms are being delievered with the intent. A String[] holding e-mail addresses that should be blind carbon copied. A String[] holding e-mail addresses that should be carbon copied. This constant is deprecated. See EXTRA_CHANGED_COMPONENT_NAME_LIST; this field will contain only the first name in the list. This field is part of ACTION_PACKAGE_CHANGED, and contains a string array of all of the components that have changed. This field is part of ACTION_EXTERNAL_APPLICATIONS_AVAILABLE, ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE and contains a string array of all of the components that have changed. This field is part of ACTION_EXTERNAL_APPLICATIONS_AVAILABLE, ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE and contains an integer array of uids of all of the components that have changed. Used as a boolean extra field in ACTION_PACKAGE_REMOVED intents to indicate whether this represents a full uninstall (removing both the code and its data) or a partial uninstall (leaving its data, implying that this is an update). Used as an int extra field in ACTION_DOCK_EVENT intents to request the dock state. Used as an int value for EXTRA_DOCK_STATE to represent that the phone is in a car dock. Used as an int value for EXTRA_DOCK_STATE to represent that the phone is in a desk dock. Used as an int value for EXTRA_DOCK_STATE to represent that the phone is not in any dock. Used as an boolean extra field in ACTION_PACKAGE_REMOVED or ACTION_PACKAGE_CHANGED intents to override the default action of restarting the application. A String[] holding e-mail addresses that should be delivered to. A Parcelable[] of Intent or LabeledIntent objects as set with putExtra(String, Parcelable[]) of additional activities to place a the front of the list of choices, when shown to the user with a

String String String

EXTRA_BCC EXTRA_CC EXTRA_CHANGED_COMPONENT_NAME

String

EXTRA_CHANGED_COMPONENT_NAME_LIST

String

EXTRA_CHANGED_PACKAGE_LIST

String

EXTRA_CHANGED_UID_LIST

String

EXTRA_DATA_REMOVED

String int int int String

EXTRA_DOCK_STATE EXTRA_DOCK_STATE_CAR EXTRA_DOCK_STATE_DESK EXTRA_DOCK_STATE_UNDOCKED EXTRA_DONT_KILL_APP

String String

EXTRA_EMAIL EXTRA_INITIAL_INTENTS

ACTION_CHOOSER. String String String EXTRA_INTENT EXTRA_KEY_EVENT EXTRA_PHONE_NUMBER An Intent describing the choices you would like shown with ACTION_PICK_ACTIVITY. A KeyEvent object containing the event that triggered the creation of the Intent it is in. A String holding the phone number originally entered in ACTION_NEW_OUTGOING_CALL, or the actual number to call in a ACTION_CALL. Used in the extra field in the remote intent. Used as a boolean extra field in ACTION_PACKAGE_REMOVED intents to indicate that this is a replacement of the package, so this broadcast will immediately be followed by an add broadcast for a different version of the same package. The name of the extra used to define the icon, as a Bitmap, of a shortcut. The name of the extra used to define the icon, as a ShortcutIconResource, of a shortcut. The name of the extra used to define the Intent of a shortcut. The name of the extra used to define the name of a shortcut. A content: URI holding a stream of data associated with the Intent, used with ACTION_SEND to supply the data being sent. A constant string holding the desired subject line of a message. The initial data to place in a newly created record. A constant CharSequence that is associated with the Intent, used with ACTION_SEND to supply the literal data to be sent. A CharSequence dialog title to provide to the user when used with a ACTION_CHOOSER. Used as an int extra field in ACTION_UID_REMOVED intents to supply the uid the package had been assigned. Use with fillIn(Intent, int) to allow the current action value to be overwritten, even if it is already set. Use with fillIn(Intent, int) to allow the current categories to be overwritten, even if they are already set. Use with fillIn(Intent, int) to allow the current component value to be overwritten, even if it is already set. Use with fillIn(Intent, int) to allow the current data or type value overwritten, even if it is already set. Use with fillIn(Intent, int) to allow the current package value to be overwritten, even if it is already set. Use with fillIn(Intent, int) to allow the current package value to be overwritten, even if it is already set. This flag is not normally set by application code, but

String String

EXTRA_REMOTE_INTENT_TOKEN EXTRA_REPLACING

String String String String String

EXTRA_SHORTCUT_ICON EXTRA_SHORTCUT_ICON_RESOURCE EXTRA_SHORTCUT_INTENT EXTRA_SHORTCUT_NAME EXTRA_STREAM

String String String

EXTRA_SUBJECT EXTRA_TEMPLATE EXTRA_TEXT

String String

EXTRA_TITLE EXTRA_UID

int

FILL_IN_ACTION

int

FILL_IN_CATEGORIES

int

FILL_IN_COMPONENT

int

FILL_IN_DATA

int

FILL_IN_PACKAGE

int

FILL_IN_SOURCE_BOUNDS

int

FLAG_ACTIVITY_BROUGHT_TO_FRONT

set for you by the system as described in the launchMode documentation for the singleTask mode. int FLAG_ACTIVITY_CLEAR_TOP If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent. If set, this marks a point in the task's activity stack that should be cleared when the task is reset. If set, the new activity is not kept in the list of recently launched activities. If set and this intent is being used to launch a new activity from an existing one, then the reply target of the existing activity will be transfered to the new activity. This flag is not normally set by application code, but set for you by the system if this activity is being launched from history (longpress home key). Do not use this flag unless you are implementing your own top-level application launcher. If set, this activity will become the start of a new task on this history stack. If set in an Intent passed to Context.startActivity(), this flag will prevent the system from applying an activity transition animation to go to the next activity state. If set, the new activity is not kept in the history stack. If set, this flag will prevent the normal onUserLeaveHint() callback from occurring on the current frontmost activity before it is paused as the newly-started activity is brought to the front. If set and this intent is being used to launch a new activity from an existing one, the current activity will not be counted as the top activity for deciding whether the new intent should be delivered to the top instead of starting a new one. If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought to the front of its task's history stack if it is already running. If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. If set, the activity will not be launched if it is already running at the top of the history stack. A flag you can enable for debugging: when set, log messages will be printed during the resolution of this intent to show you what has been found to create the final resolved list. Can be set by the caller to indicate that this Intent is coming from a background operation, not from direct user interaction. If set, the recipient of this Intent will be granted permission to perform read operations on the Uri in the Intent's data. If set, the recipient of this Intent will be granted permission to perform write operations on the Uri in

int int int

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS FLAG_ACTIVITY_FORWARD_RESULT

int

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY

int int int

FLAG_ACTIVITY_MULTIPLE_TASK FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NO_ANIMATION

int int

FLAG_ACTIVITY_NO_HISTORY FLAG_ACTIVITY_NO_USER_ACTION

int

FLAG_ACTIVITY_PREVIOUS_IS_TOP

int

FLAG_ACTIVITY_REORDER_TO_FRONT

int

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

int int

FLAG_ACTIVITY_SINGLE_TOP FLAG_DEBUG_LOG_RESOLUTION

int

FLAG_FROM_BACKGROUND

int

FLAG_GRANT_READ_URI_PERMISSION

int

FLAG_GRANT_WRITE_URI_PERMISSION

the Intent's data. int FLAG_RECEIVER_REGISTERED_ONLY If set, when sending a broadcast only registered receivers will be called -- no BroadcastReceiver components will be launched. If set, when sending a broadcast the new broadcast will replace any existing pending broadcast that matches it. Boolean that can be supplied as meta-data with a dock activity, to indicate that the dock should take over the home key when it is active. Flag for use with toUri(int) and parseUri (String, int): the URI string always has the "intent:" scheme.
[Expand]

int

FLAG_RECEIVER_REPLACE_PENDING

String

METADATA_DOCK_HOME

int

URI_INTENT_SCHEME

Inherited Constants From interface android.os.Parcelable Public Constructors Intent () Create an empty intent. Intent (Intent o) Copy constructor. Intent (String action) Create an intent with a given action. Intent (String action, Uri uri) Create an intent with a given action and for a given data url. Intent (Context packageContext, Class<?> cls) Create an intent for a specific component. Intent (String action, Uri uri, Context packageContext, Class<?> cls) Create an intent for a specific component with a specified action and data. Public Methods Intent addCategory (String category) Add a new category to the intent. addFlags (int flags) Add additional flags to the intent (or with existing flags value). clone () Creates and returns a copy of this Object.

Intent

Object

Intent

cloneFilter () Make a clone of only the parts of the Intent that are relevant for filter matching: the action, data, type, component, and categories. createChooser (Intent target, CharSequence title) Convenience function for creating a ACTION_CHOOSER Intent. describeContents () Describe the kinds of special objects contained in this Parcelable's marshalled representation. fillIn (Intent other, int flags) Copy the contents of other in to this object, but only where fields are not defined by this object. filterEquals (Intent other) Determine if two intents are the same for the purposes of intent resolution (filtering).

static Intent

int

int

boolean

int

filterHashCode () Generate hash code that matches semantics of filterEquals(). getAction () Retrieve the general action to be performed, such as ACTION_VIEW. getBooleanArrayExtra (String name) Retrieve extended data from the intent. getBooleanExtra (String name, boolean defaultValue) Retrieve extended data from the intent. getBundleExtra (String name) Retrieve extended data from the intent. getByteArrayExtra (String name) Retrieve extended data from the intent. getByteExtra (String name, byte defaultValue) Retrieve extended data from the intent. getCategories () Return the set of all categories in the intent. getCharArrayExtra (String name) Retrieve extended data from the intent. getCharExtra (String name, char defaultValue) Retrieve extended data from the intent. getCharSequenceArrayExtra (String name) Retrieve extended data from the intent. getCharSequenceArrayListExtra (String name) Retrieve extended data from the intent. getCharSequenceExtra (String name) Retrieve extended data from the intent. getComponent () Retrieve the concrete component associated with the intent. getData () Retrieve data this intent is operating on. getDataString () The same as getData(), but returns the URI as an encoded String. getDoubleArrayExtra (String name) Retrieve extended data from the intent. getDoubleExtra (String name, double defaultValue) Retrieve extended data from the intent. getExtras () Retrieves a map of extended data from the intent. getFlags () Retrieve any special flags associated with this intent. getFloatArrayExtra (String name) Retrieve extended data from the intent. getFloatExtra (String name, float defaultValue) Retrieve extended data from the intent. getIntArrayExtra (String name) Retrieve extended data from the intent. getIntExtra (String name, int defaultValue) Retrieve extended data from the intent.

String

boolean[]

boolean

Bundle

byte[]

byte

Set<String>

char[]

char

CharSequence[]

ArrayList<CharSequence>

CharSequence

ComponentName

Uri

String

double[]

double

Bundle

int

float[]

float

int[]

int

ArrayList<Integer>

getIntegerArrayListExtra (String name) Retrieve extended data from the intent. getIntent (String uri) This method is deprecated. Use parseUri(String, int) instead. getIntentOld (String uri) getLongArrayExtra (String name) Retrieve extended data from the intent. getLongExtra (String name, long defaultValue) Retrieve extended data from the intent. getPackage () Retrieve the application package name this Intent is limited to. getParcelableArrayExtra (String name) Retrieve extended data from the intent. getParcelableArrayListExtra (String name) Retrieve extended data from the intent. getParcelableExtra (String name) Retrieve extended data from the intent. getScheme () Return the scheme portion of the intent's data. getSerializableExtra (String name) Retrieve extended data from the intent. getShortArrayExtra (String name) Retrieve extended data from the intent. getShortExtra (String name, short defaultValue) Retrieve extended data from the intent. getSourceBounds () Get the bounds of the sender of this intent, in screen coordinates. getStringArrayExtra (String name) Retrieve extended data from the intent. getStringArrayListExtra (String name) Retrieve extended data from the intent. getStringExtra (String name) Retrieve extended data from the intent. getType () Retrieve any explicit MIME type included in the intent. hasCategory (String category) Check if an category exists in the intent. hasExtra (String name) Returns true if an extra value is associated with the given name. hasFileDescriptors () Returns true if the Intent's extras contain a parcelled file descriptor. parseIntent (Resources resources, XmlPullParser parser, AttributeSet attrs) Parses the "intent" element (and its children) from XML and instantiates an Intent object. parseUri (String uri, int flags) Create an intent from a URI. putCharSequenceArrayListExtra (String name, ArrayList<CharSequence> value) Add extended data to the intent.

static Intent

static Intent long[]

long

String

Parcelable[]

<T extends Parcelable> ArrayList<T>

<T extends Parcelable> T

String

Serializable

short[]

short

Rect

String[]

ArrayList<String>

String

String

boolean

boolean

boolean

static Intent

static Intent

Intent

Intent

putExtra (String name, String[] value) Add extended data to the intent. putExtra (String name, Parcelable value) Add extended data to the intent. putExtra (String name, long value) Add extended data to the intent. putExtra (String name, boolean value) Add extended data to the intent. putExtra (String name, double value) Add extended data to the intent. putExtra (String name, CharSequence[] value) Add extended data to the intent. putExtra (String name, Parcelable[] value) Add extended data to the intent. putExtra (String name, char value) Add extended data to the intent. putExtra (String name, int[] value) Add extended data to the intent. putExtra (String name, int value) Add extended data to the intent. putExtra (String name, double[] value) Add extended data to the intent. putExtra (String name, float value) Add extended data to the intent. putExtra (String name, short value) Add extended data to the intent. putExtra (String name, long[] value) Add extended data to the intent. putExtra (String name, boolean[] value) Add extended data to the intent. putExtra (String name, short[] value) Add extended data to the intent. putExtra (String name, String value) Add extended data to the intent. putExtra (String name, Serializable value) Add extended data to the intent. putExtra (String name, float[] value) Add extended data to the intent. putExtra (String name, Bundle value) Add extended data to the intent. putExtra (String name, byte[] value) Add extended data to the intent. putExtra (String name, CharSequence value) Add extended data to the intent. putExtra (String name, char[] value) Add extended data to the intent. putExtra (String name, byte value) Add extended data to the intent.

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

Intent

putExtras (Intent src) Copy all extras in 'src' in to this intent. putExtras (Bundle extras) Add a set of extended data to the intent. putIntegerArrayListExtra (String name, ArrayList<Integer> value) Add extended data to the intent. putParcelableArrayListExtra (String name, ArrayList<? extends Parcelable> value) Add extended data to the intent. putStringArrayListExtra (String name, ArrayList<String> value) Add extended data to the intent. readFromParcel (Parcel in) removeCategory (String category) Remove an category from an intent. removeExtra (String name) Remove extended data from the intent. replaceExtras (Intent src) Completely replace the extras in the Intent with the extras in the given Intent. replaceExtras (Bundle extras) Completely replace the extras in the Intent with the given Bundle of extras. resolveActivity (PackageManager pm) Return the Activity component that should be used to handle this intent. resolveActivityInfo (PackageManager pm, int flags) Resolve the Intent into an ActivityInfo describing the activity that should execute the intent. resolveType (ContentResolver resolver) Return the MIME data type of this intent. resolveType (Context context) Return the MIME data type of this intent. resolveTypeIfNeeded (ContentResolver resolver) Return the MIME data type of this intent, only if it will be needed for intent resolution. setAction (String action) Set the general action to be performed. setClass (Context packageContext, Class<?> cls) Convenience for calling setComponent(ComponentName) with the name returned by a Class object. setClassName (String packageName, String className) Convenience for calling setComponent(ComponentName) with an explicit application package name and class name. setClassName (Context packageContext, String className) Convenience for calling setComponent(ComponentName) with an explicit class name. setComponent (ComponentName component) (Usually optional) Explicitly set the component to handle the intent. setData (Uri data) Set the data this intent is operating on. setDataAndType (Uri data, String type) (Usually optional) Set the data for the intent along with an explicit MIME data type. setExtrasClassLoader (ClassLoader loader) Sets the ClassLoader that will be used when unmarshalling any Parcelable values from the extras of this Intent.

Intent

Intent

Intent

Intent

void void

void

Intent

Intent

ComponentName

ActivityInfo

String

String

String

Intent

Intent

Intent

Intent

Intent

Intent

Intent

void

Intent

setFlags (int flags) Set special flags controlling how this intent is handled. setPackage (String packageName) (Usually optional) Set an explicit application package name that limits the components this Intent will resolve to. setSourceBounds (Rect r) Set the bounds of the sender of this intent, in screen coordinates. setType (String type) Set an explicit MIME data type. toString () Returns a string containing a concise, human-readable description of this object. toURI () This method is deprecated. Use toUri(int) instead. toUri (int flags) Convert this Intent into a String holding a URI representation of it. writeToParcel (Parcel out, int flags) Flatten this object in to a Parcel.
[Expand]

Intent

void

Intent

String

String

String

void

Inherited Methods From class java.lang.Object From interface android.os.Parcelable

Constants
public static final String ACTION_AIRPLANE_MODE_CHANGED
Since: API Level 1

Broadcast Action: The user has switched the phone into or out of Airplane Mode. One or more radios have been turned off or on. The intent will have the following extra value:

state - A boolean value indicating whether Airplane Mode is on. If true, then cell radio and possibly other radios such as bluetooth or WiFi may have also been turned off

This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.AIRPLANE_MODE"
public static final String ACTION_ALL_APPS
Since: API Level 1

Activity Action: List all available applications Input: Nothing. Output: nothing. Constant Value: "android.intent.action.ALL_APPS"
public static final String ACTION_ANSWER
Since: API Level 1

Activity Action: Handle an incoming phone call. Input: nothing. Output: nothing. Constant Value: "android.intent.action.ANSWER"
public static final String ACTION_ATTACH_DATA

Used to indicate that some piece of data should be attached to some other place. For example, image data could be attached to a contact. It is up to the recipient to decide where the data should be attached; the intent does not specify the ultimate destination. Input: getData() is URI of data to be attached. Output: nothing. Constant Value: "android.intent.action.ATTACH_DATA"
public static final String ACTION_BATTERY_CHANGED
Since: API Level 1

Broadcast Action: This is a sticky broadcast containing the charging state, level, and other information about the battery. See BatteryManager for documentation on the contents of the Intent. You can not receive this through components declared in manifests, only by explicitly registering for it with Context.registerReceiver(). See ACTION_BATTERY_LOW, ACTION_BATTERY_OKAY, ACTION_POWER_CONNECTED, and ACTION_POWER_DISCONNECTED for distinct battery-related broadcasts that are sent and can be received through manifest receivers. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.BATTERY_CHANGED"
public static final String ACTION_BATTERY_LOW
Since: API Level 1

Broadcast Action: Indicates low battery condition on the device. This broadcast corresponds to the "Low battery warning" system dialog. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.BATTERY_LOW"
public static final String ACTION_BATTERY_OKAY
Since: API Level 4

Broadcast Action: Indicates the battery is now okay after being low. This will be sent after ACTION_BATTERY_LOW once the battery has gone back up to an okay state. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.BATTERY_OKAY"
public static final String ACTION_BOOT_COMPLETED
Since: API Level 1

Broadcast Action: This is broadcast once, after the system has finished booting. It can be used to perform application-specific initialization, such as installing alarms. You must hold the RECEIVE_BOOT_COMPLETED permission in order to receive this broadcast. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.BOOT_COMPLETED"
public static final String ACTION_BUG_REPORT
Since: API Level 1

Activity Action: Show activity for reporting a bug. Input: Nothing. Output: Nothing. Constant Value: "android.intent.action.BUG_REPORT"
public static final String ACTION_CALL
Since: API Level 1

Activity Action: Perform a call to someone specified by the data. Input: If nothing, an empty dialer is started; else getData() is URI of a phone number to be dialed or a tel: URI of an explicit

phone number. Output: nothing. Note: there will be restrictions on which applications can initiate a call; most applications should use the ACTION_DIAL. Note: this Intent cannot be used to call emergency numbers. Applications can dial emergency numbers using ACTION_DIAL, however. Constant Value: "android.intent.action.CALL"
public static final String ACTION_CALL_BUTTON
Since: API Level 1

Activity Action: The user pressed the "call" button to go to the dialer or other appropriate UI for placing a call. Input: Nothing. Output: Nothing. Constant Value: "android.intent.action.CALL_BUTTON"
public static final String ACTION_CAMERA_BUTTON
Since: API Level 1

Broadcast Action: The "Camera Button" was pressed. Includes a single extra field, EXTRA_KEY_EVENT, containing the key event that caused the broadcast. Constant Value: "android.intent.action.CAMERA_BUTTON"
public static final String ACTION_CHOOSER
Since: API Level 1

Activity Action: Display an activity chooser, allowing the user to pick what they want to before proceeding. This can be used as an alternative to the standard activity picker that is displayed by the system when you try to start an activity with multiple possible matches, with these differences in behavior:

You can specify the title that will appear in the activity chooser. The user does not have the option to make one of the matching activities a preferred activity, and all possible activities will always be shown even if one of them is currently marked as the preferred activity.

This action should be used when the user will naturally expect to select an activity in order to proceed. An example if when not to use it is when the user clicks on a "mailto:" link. They would naturally expect to go directly to their mail app, so startActivity() should be called directly: it will either launch the current preferred app, or put up a dialog allowing the user to pick an app to use and optionally marking that as preferred. In contrast, if the user is selecting a menu item to send a picture they are viewing to someone else, there are many different things they may want to do at this point: send it through e-mail, upload it to a web service, etc. In this case the CHOOSER action should be used, to always present to the user a list of the things they can do, with a nice title given by the caller such as "Send this photo with:". As a convenience, an Intent of this form can be created with the createChooser(Intent, CharSequence) function. Input: No data should be specified. get*Extra must have a EXTRA_INTENT field containing the Intent being executed, and can optionally have a EXTRA_TITLE field containing the title text to display in the chooser. Output: Depends on the protocol of EXTRA_INTENT. Constant Value: "android.intent.action.CHOOSER"
public static final String ACTION_CLOSE_SYSTEM_DIALOGS
Since: API Level 1

Broadcast Action: This is broadcast when a user action should request a temporary system dialog to dismiss. Some examples of temporary system dialogs are the notification window-shade and the recent tasks dialog. Constant Value: "android.intent.action.CLOSE_SYSTEM_DIALOGS"
public static final String ACTION_CONFIGURATION_CHANGED
Since: API Level 1

Broadcast Action: The current device Configuration (orientation, locale, etc) has changed. When such a change happens, the UIs (view hierarchy) will need to be rebuilt based on this new information; for the most part, applications don't need to worry about this, because the system will take care of stopping and restarting the application to make sure it sees the new changes. Some system code that can not be restarted will need to watch for this action and handle it appropriately.

You can not receive this through components declared in manifests, only by explicitly registering for it with Context.registerReceiver(). This is a protected intent that can only be sent by the system. See Also Configuration Constant Value: "android.intent.action.CONFIGURATION_CHANGED"
public static final String ACTION_CREATE_SHORTCUT
Since: API Level 1

Activity Action: Creates a shortcut. Input: Nothing. Output: An Intent representing the shortcut. The intent must contain three extras: SHORTCUT_INTENT (value: Intent), SHORTCUT_NAME (value: String), and SHORTCUT_ICON (value: Bitmap) or SHORTCUT_ICON_RESOURCE (value: ShortcutIconResource). See Also EXTRA_SHORTCUT_INTENT EXTRA_SHORTCUT_NAME EXTRA_SHORTCUT_ICON EXTRA_SHORTCUT_ICON_RESOURCE Intent.ShortcutIconResource Constant Value: "android.intent.action.CREATE_SHORTCUT"
public static final String ACTION_DATE_CHANGED
Since: API Level 1

Broadcast Action: The date has changed. Constant Value: "android.intent.action.DATE_CHANGED"


public static final String ACTION_DEFAULT
Since: API Level 1

A synonym for ACTION_VIEW, the "standard" action that is performed on a piece of data. Constant Value: "android.intent.action.VIEW"
public static final String ACTION_DELETE
Since: API Level 1

Activity Action: Delete the given data from its container. Input: getData() is URI of data to be deleted. Output: nothing. Constant Value: "android.intent.action.DELETE"
public static final String ACTION_DEVICE_STORAGE_LOW
Since: API Level 1

Broadcast Action: A sticky broadcast that indicates low memory condition on the device This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.DEVICE_STORAGE_LOW"
public static final String ACTION_DEVICE_STORAGE_OK
Since: API Level 1

Broadcast Action: Indicates low memory condition on the device no longer exists This is a protected intent that can only be sent by the system.

Constant Value: "android.intent.action.DEVICE_STORAGE_OK"


public static final String ACTION_DIAL
Since: API Level 1

Activity Action: Dial a number as specified by the data. This shows a UI with the number being dialed, allowing the user to explicitly initiate the call. Input: If nothing, an empty dialer is started; else getData() is URI of a phone number to be dialed or a tel: URI of an explicit phone number. Output: nothing. Constant Value: "android.intent.action.DIAL"
public static final String ACTION_DOCK_EVENT
Since: API Level 5

Broadcast Action: A sticky broadcast for changes in the physical docking state of the device. The intent will have the following extra values:

EXTRA_DOCK_STATE - the current dock state, indicating which dock the device is physically in.

This is intended for monitoring the current physical dock state. See UiModeManager for the normal API dealing with dock mode changes. Constant Value: "android.intent.action.DOCK_EVENT"
public static final String ACTION_EDIT
Since: API Level 1

Activity Action: Provide explicit editable access to the given data. Input: getData() is URI of data to be edited. Output: nothing. Constant Value: "android.intent.action.EDIT"
public static final String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
Since: API Level 8

Broadcast Action: Resources for a set of packages (which were previously unavailable) are currently available since the media on which they exist is available. The extra data EXTRA_CHANGED_PACKAGE_LIST contains a list of packages whose availability changed. The extra data EXTRA_CHANGED_UID_LIST contains a list of uids of packages whose availability changed. Note that the packages in this list do not receive this broadcast. The specified set of packages are now available on the system. Includes the following extras:

EXTRA_CHANGED_PACKAGE_LIST is the set of packages whose resources(were previously unavailable) are currently available. EXTRA_CHANGED_UID_LIST is the set of uids of the packages whose resources(were previously unavailable) are currently available.

This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE"
public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE
Since: API Level 8

Broadcast Action: Resources for a set of packages are currently unavailable since the media on which they exist is unavailable. The extra data EXTRA_CHANGED_PACKAGE_LIST contains a list of packages whose availability changed. The extra data EXTRA_CHANGED_UID_LIST contains a list of uids of packages whose availability changed. The specified set of packages can no longer be launched and are practically unavailable on the system. Inclues the following extras:

EXTRA_CHANGED_PACKAGE_LIST is the set of packages whose resources are no longer available. EXTRA_CHANGED_UID_LIST is the set of packages whose resources are no longer available.

This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE"

public static final String ACTION_FACTORY_TEST

Since: API Level 1

Activity Action: Main entry point for factory tests. Only used when the device is booting in factory test node. The implementing package must be installed in the system image. Input: nothing Output: nothing Constant Value: "android.intent.action.FACTORY_TEST"
public static final String ACTION_GET_CONTENT
Since: API Level 1

Activity Action: Allow the user to select a particular kind of data and return it. This is different than ACTION_PICK in that here we just say what kind of data is desired, not a URI of existing data from which the user can pick. A ACTION_GET_CONTENT could allow the user to create the data as it runs (for example taking a picture or recording a sound), let them browser over the web and download the desired data, etc. There are two main ways to use this action: if you want an specific kind of data, such as a person contact, you set the MIME type to the kind of data you want and launch it with startActivity(Intent). The system will then launch the best application to select that kind of data for you. You may also be interested in any of a set of types of content the user can pick. For example, an e-mail application that wants to allow the user to add an attachment to an e-mail message can use this action to bring up a list of all of the types of content the user can attach. In this case, you should wrap the GET_CONTENT intent with a chooser (through createChooser(Intent, CharSequence)), which will give the proper interface for the user to pick how to send your data and allow you to specify a prompt indicating what they are doing. You will usually specify a broad MIME type (such as image/* or */*), resulting in a broad range of content types the user can select from. When using such a broad GET_CONTENT action, it is often desireable to only pick from data that can be represented as a stream. This is accomplished by requiring the CATEGORY_OPENABLE in the Intent. Input: getType() is the desired MIME type to retrieve. Note that no URI is supplied in the intent, as there are no constraints on where the returned data originally comes from. You may also include the CATEGORY_OPENABLE if you can only accept data that can be opened as a stream. Output: The URI of the item that was picked. This must be a content: URI so that any receiver can access it. Constant Value: "android.intent.action.GET_CONTENT"
public static final String ACTION_GTALK_SERVICE_CONNECTED
Since: API Level 1

Broadcast Action: An GTalk connection has been established. Constant Value: "android.intent.action.GTALK_CONNECTED"
public static final String ACTION_GTALK_SERVICE_DISCONNECTED
Since: API Level 1

Broadcast Action: An GTalk connection has been disconnected. Constant Value: "android.intent.action.GTALK_DISCONNECTED"
public static final String ACTION_HEADSET_PLUG
Since: API Level 1

Broadcast Action: Wired Headset plugged in or unplugged. The intent will have the following extra values:

state - 0 for unplugged, 1 for plugged. name - Headset type, human readable string microphone - 1 if headset has a microphone, 0 otherwise

Constant Value: "android.intent.action.HEADSET_PLUG"


public static final String ACTION_INPUT_METHOD_CHANGED

Broadcast Action: An input method has been changed. Constant Value: "android.intent.action.INPUT_METHOD_CHANGED"
public static final String ACTION_INSERT
Since: API Level 1

Activity Action: Insert an empty item into the given container. Input: getData() is URI of the directory (vnd.android.cursor.dir/*) in which to place the data. Output: URI of the new data that was created. Constant Value: "android.intent.action.INSERT"
public static final String ACTION_INSERT_OR_EDIT
Since: API Level 1

Activity Action: Pick an existing item, or insert a new item, and then edit it. Input: getType() is the desired MIME type of the item to create or edit. The extras can contain type specific data to pass through to the editing/creating activity. Output: The URI of the item that was picked. This must be a content: URI so that any receiver can access it. Constant Value: "android.intent.action.INSERT_OR_EDIT"
public static final String ACTION_LOCALE_CHANGED
Since: API Level 7

Broadcast Action: The current device's locale has changed. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.LOCALE_CHANGED"
public static final String ACTION_MAIN
Since: API Level 1

Activity Action: Start as a main entry point, does not expect to receive data. Input: nothing Output: nothing Constant Value: "android.intent.action.MAIN"
public static final String ACTION_MANAGE_PACKAGE_STORAGE
Since: API Level 1

Broadcast Action: Indicates low memory condition notification acknowledged by user and package management should be started. This is triggered by the user from the ACTION_DEVICE_STORAGE_LOW notification. Constant Value: "android.intent.action.MANAGE_PACKAGE_STORAGE"
public static final String ACTION_MEDIA_BAD_REMOVAL
Since: API Level 1

Broadcast Action: External media was removed from SD card slot, but mount point was not unmounted. The path to the mount point for the removed media is contained in the Intent.mData field. Constant Value: "android.intent.action.MEDIA_BAD_REMOVAL"
public static final String ACTION_MEDIA_BUTTON
Since: API Level 1

Broadcast Action: The "Media Button" was pressed. Includes a single extra field, EXTRA_KEY_EVENT, containing the key event that caused the broadcast. Constant Value: "android.intent.action.MEDIA_BUTTON"
public static final String ACTION_MEDIA_CHECKING
Since: API Level 3

Broadcast Action: External media is present, and being disk-checked The path to the mount point for the checking media is contained in the Intent.mData field.

Constant Value: "android.intent.action.MEDIA_CHECKING"


public static final String ACTION_MEDIA_EJECT
Since: API Level 1

Broadcast Action: User has expressed the desire to remove the external storage media. Applications should close all files they have open within the mount point when they receive this intent. The path to the mount point for the media to be ejected is contained in the Intent.mData field. Constant Value: "android.intent.action.MEDIA_EJECT"
public static final String ACTION_MEDIA_MOUNTED
Since: API Level 1

Broadcast Action: External media is present and mounted at its mount point. The path to the mount point for the removed media is contained in the Intent.mData field. The Intent contains an extra with name "read-only" and Boolean value to indicate if the media was mounted read only. Constant Value: "android.intent.action.MEDIA_MOUNTED"
public static final String ACTION_MEDIA_NOFS
Since: API Level 3

Broadcast Action: External media is present, but is using an incompatible fs (or is blank) The path to the mount point for the checking media is contained in the Intent.mData field. Constant Value: "android.intent.action.MEDIA_NOFS"
public static final String ACTION_MEDIA_REMOVED
Since: API Level 1

Broadcast Action: External media has been removed. The path to the mount point for the removed media is contained in the Intent.mData field. Constant Value: "android.intent.action.MEDIA_REMOVED"
public static final String ACTION_MEDIA_SCANNER_FINISHED
Since: API Level 1

Broadcast Action: The media scanner has finished scanning a directory. The path to the scanned directory is contained in the Intent.mData field. Constant Value: "android.intent.action.MEDIA_SCANNER_FINISHED"
public static final String ACTION_MEDIA_SCANNER_SCAN_FILE
Since: API Level 1

Broadcast Action: Request the media scanner to scan a file and add it to the media database. The path to the file is contained in the Intent.mData field. Constant Value: "android.intent.action.MEDIA_SCANNER_SCAN_FILE"
public static final String ACTION_MEDIA_SCANNER_STARTED
Since: API Level 1

Broadcast Action: The media scanner has started scanning a directory. The path to the directory being scanned is contained in the Intent.mData field. Constant Value: "android.intent.action.MEDIA_SCANNER_STARTED"
public static final String ACTION_MEDIA_SHARED
Since: API Level 1

Broadcast Action: External media is unmounted because it is being shared via USB mass storage. The path to the mount point for the shared media is contained in the Intent.mData field. Constant Value: "android.intent.action.MEDIA_SHARED"
public static final String ACTION_MEDIA_UNMOUNTABLE
Since: API Level 1

Broadcast Action: External media is present but cannot be mounted. The path to the mount point for the removed media is contained in the Intent.mData field.

Constant Value: "android.intent.action.MEDIA_UNMOUNTABLE"


public static final String ACTION_MEDIA_UNMOUNTED
Since: API Level 1

Broadcast Action: External media is present, but not mounted at its mount point. The path to the mount point for the removed media is contained in the Intent.mData field. Constant Value: "android.intent.action.MEDIA_UNMOUNTED"
public static final String ACTION_NEW_OUTGOING_CALL
Since: API Level 1

Broadcast Action: An outgoing call is about to be placed. The Intent will have the following extra value:

EXTRA_PHONE_NUMBER - the phone number originally intended to be dialed.

Once the broadcast is finished, the resultData is used as the actual number to call. If null, no call will be placed. It is perfectly acceptable for multiple receivers to process the outgoing call in turn: for example, a parental control application might verify that the user is authorized to place the call at that time, then a number-rewriting application might add an area code if one was not specified. For consistency, any receiver whose purpose is to prohibit phone calls should have a priority of 0, to ensure it will see the final phone number to be dialed. Any receiver whose purpose is to rewrite phone numbers to be called should have a positive priority. Negative priorities are reserved for the system for this broadcast; using them may cause problems. Any BroadcastReceiver receiving this Intent must not abort the broadcast. Emergency calls cannot be intercepted using this mechanism, and other calls cannot be modified to call emergency numbers using this mechanism. You must hold the PROCESS_OUTGOING_CALLS permission to receive this Intent. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.NEW_OUTGOING_CALL"
public static final String ACTION_PACKAGE_ADDED
Since: API Level 1

Broadcast Action: A new application package has been installed on the device. The data contains the name of the package. Note that the newly installed package does not receive this broadcast. My include the following extras:

EXTRA_UID containing the integer uid assigned to the new package. EXTRA_REPLACING is set to true if this is following an ACTION_PACKAGE_REMOVED broadcast for the same package.

This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.PACKAGE_ADDED"
public static final String ACTION_PACKAGE_CHANGED
Since: API Level 1

Broadcast Action: An existing application package has been changed (e.g. a component has been enabled or disabled). The data contains the name of the package.

EXTRA_UID containing the integer uid assigned to the package. EXTRA_CHANGED_COMPONENT_NAME_LIST containing the class name of the changed components. EXTRA_DONT_KILL_APP containing boolean field to override the default action of restarting the application.

This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.PACKAGE_CHANGED"
public static final String ACTION_PACKAGE_DATA_CLEARED
Since: API Level 3

Broadcast Action: The user has cleared the data of a package. This should be preceded by ACTION_PACKAGE_RESTARTED, after which all of its persistent data is erased and this broadcast sent. Note that the cleared package does not receive this broadcast.

The data contains the name of the package.

EXTRA_UID containing the integer uid assigned to the package.

This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.PACKAGE_DATA_CLEARED"
public static final String ACTION_PACKAGE_INSTALL
Since: API Level 1

Broadcast Action: Trigger the download and eventual installation of a package. Input: getData() is the URI of the package file to download. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.PACKAGE_INSTALL"
public static final String ACTION_PACKAGE_REMOVED
Since: API Level 1

Broadcast Action: An existing application package has been removed from the device. The data contains the name of the package. The package that is being installed does not receive this Intent.

EXTRA_UID containing the integer uid previously assigned to the package. EXTRA_DATA_REMOVED is set to true if the entire application -- data and code -- is being removed. EXTRA_REPLACING is set to true if this will be followed by an ACTION_PACKAGE_ADDED broadcast for the same package.

This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.PACKAGE_REMOVED"
public static final String ACTION_PACKAGE_REPLACED
Since: API Level 3

Broadcast Action: A new version of an application package has been installed, replacing an existing version that was previously installed. The data contains the name of the package. My include the following extras:

EXTRA_UID containing the integer uid assigned to the new package.

This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.PACKAGE_REPLACED"
public static final String ACTION_PACKAGE_RESTARTED
Since: API Level 1

Broadcast Action: The user has restarted a package, and all of its processes have been killed. All runtime state associated with it (processes, alarms, notifications, etc) should be removed. Note that the restarted package does not receive this broadcast. The data contains the name of the package.

EXTRA_UID containing the integer uid assigned to the package.

This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.PACKAGE_RESTARTED"
public static final String ACTION_PICK
Since: API Level 1

Activity Action: Pick an item from the data, returning what was selected. Input: getData() is URI containing a directory of data (vnd.android.cursor.dir/*) from which to pick an item. Output: The URI of the item that was picked. Constant Value: "android.intent.action.PICK"
public static final String ACTION_PICK_ACTIVITY
Since: API Level 1

Activity Action: Pick an activity given an intent, returning the class selected. Input: get*Extra field EXTRA_INTENT is an Intent used with queryIntentActivities(Intent, int) to determine the set of activities from which to pick. Output: Class name of the activity that was selected. Constant Value: "android.intent.action.PICK_ACTIVITY"
public static final String ACTION_POWER_CONNECTED
Since: API Level 4

Broadcast Action: External power has been connected to the device. This is intended for applications that wish to register specifically to this notification. Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to stay active to receive this notification. This action can be used to implement actions that wait until power is available to trigger. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.ACTION_POWER_CONNECTED"
public static final String ACTION_POWER_DISCONNECTED
Since: API Level 4

Broadcast Action: External power has been removed from the device. This is intended for applications that wish to register specifically to this notification. Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to stay active to receive this notification. This action can be used to implement actions that wait until power is available to trigger. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.ACTION_POWER_DISCONNECTED"
public static final String ACTION_POWER_USAGE_SUMMARY
Since: API Level 4

Activity Action: Show power usage information to the user. Input: Nothing. Output: Nothing. Constant Value: "android.intent.action.POWER_USAGE_SUMMARY"
public static final String ACTION_PROVIDER_CHANGED
Since: API Level 1

Broadcast Action: Some content providers have parts of their namespace where they publish new events or items that the user may be especially interested in. For these things, they may broadcast this action when the set of interesting items change. For example, GmailProvider sends this notification when the set of unread mail in the inbox changes. The data of the intent identifies which part of which provider changed. When queried through the content resolver, the data URI will return the data set in question. The intent will have the following extra values: count - The number of items in the data set. This is the same as the number of items in the cursor returned by querying the data URI. This intent will be sent at boot (if the count is non-zero) and when the data set changes. It is possible for the data set to change without the count changing (for example, if a new unread message arrives in the same sync operation in which a message is archived). The phone should still ring/vibrate/etc as normal in this case.

Constant Value: "android.intent.action.PROVIDER_CHANGED"


public static final String ACTION_REBOOT
Since: API Level 1

Broadcast Action: Have the device reboot. This is only for use by system code. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.REBOOT"
public static final String ACTION_RUN

Activity Action: Run the data, whatever that means. Input: ? (Note: this is currently specific to the test harness.) Output: nothing. Constant Value: "android.intent.action.RUN"
public static final String ACTION_SCREEN_OFF
Since: API Level 1

Broadcast Action: Sent after the screen turns off. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.SCREEN_OFF"
public static final String ACTION_SCREEN_ON
Since: API Level 1

Broadcast Action: Sent after the screen turns on. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.SCREEN_ON"
public static final String ACTION_SEARCH
Since: API Level 1

Activity Action: Perform a search. Input: getStringExtra(SearchManager.QUERY) is the text to search for. If empty, simply enter your search results Activity with the search UI activated. Output: nothing. Constant Value: "android.intent.action.SEARCH"
public static final String ACTION_SEARCH_LONG_PRESS
Since: API Level 3

Activity Action: Start action associated with long pressing on the search key. Input: Nothing. Output: Nothing. Constant Value: "android.intent.action.SEARCH_LONG_PRESS"
public static final String ACTION_SEND
Since: API Level 1

Activity Action: Deliver some data to someone else. Who the data is being delivered to is not specified; it is up to the receiver of this action to ask the user where the data should be sent. When launching a SEND intent, you should usually wrap it in a chooser (through createChooser(Intent, CharSequence)), which will give the proper interface for the user to pick how to send your data and allow you to specify a prompt indicating what they are doing. Input: getType() is the MIME type of the data being sent. get*Extra can have either a EXTRA_TEXT or EXTRA_STREAM field, containing the data to be sent. If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the MIME type of the data in EXTRA_STREAM. Use */* if the MIME type is unknown (this will only allow senders that can handle generic data streams). Optional standard extras, which may be interpreted by some recipients as appropriate, are: EXTRA_EMAIL, EXTRA_CC, EXTRA_BCC, EXTRA_SUBJECT. Output: nothing. Constant Value: "android.intent.action.SEND"
public static final String ACTION_SENDTO
Since: API Level 1

Activity Action: Send a message to someone specified by the data.

Input: getData() is URI describing the target. Output: nothing. Constant Value: "android.intent.action.SENDTO"
public static final String ACTION_SEND_MULTIPLE
Since: API Level 4

Activity Action: Deliver multiple data to someone else. Like ACTION_SEND, except the data is multiple. Input: getType() is the MIME type of the data being sent. get*ArrayListExtra can have either a EXTRA_TEXT or EXTRA_STREAM field, containing the data to be sent. Multiple types are supported, and receivers should handle mixed types whenever possible. The right way for the receiver to check them is to use the content resolver on each URI. The intent sender should try to put the most concrete mime type in the intent type, but it can fall back to <type>/* or */* as needed. e.g. if you are sending image/jpg and image/jpg, the intent's type can be image/jpg, but if you are sending image/jpg and image/png, then the intent's type should be image/*. Optional standard extras, which may be interpreted by some recipients as appropriate, are: EXTRA_EMAIL, EXTRA_CC, EXTRA_BCC, EXTRA_SUBJECT. Output: nothing. Constant Value: "android.intent.action.SEND_MULTIPLE"
public static final String ACTION_SET_WALLPAPER
Since: API Level 1

Activity Action: Show settings for choosing wallpaper Input: Nothing. Output: Nothing. Constant Value: "android.intent.action.SET_WALLPAPER"
public static final String ACTION_SHUTDOWN
Since: API Level 4

Broadcast Action: Device is shutting down. This is broadcast when the device is being shut down (completely turned off, not sleeping). Once the broadcast is complete, the final shutdown will proceed and all unsaved data lost. Apps will not normally need to handle this, since the foreground activity will be paused as well. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.ACTION_SHUTDOWN"
public static final String ACTION_SYNC
Since: API Level 1

Activity Action: Perform a data synchronization. Input: ? Output: ? Constant Value: "android.intent.action.SYNC"
public static final String ACTION_SYSTEM_TUTORIAL
Since: API Level 3

Activity Action: Start the platform-defined tutorial Input: getStringExtra(SearchManager.QUERY) is the text to search for. If empty, simply enter your search results Activity with the search UI activated. Output: nothing. Constant Value: "android.intent.action.SYSTEM_TUTORIAL"
public static final String ACTION_TIMEZONE_CHANGED
Since: API Level 1

Broadcast Action: The timezone has changed. The intent will have the following extra values:

time-zone - The java.util.TimeZone.getID() value identifying the new time zone.

This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.TIMEZONE_CHANGED"
public static final String ACTION_TIME_CHANGED
Since: API Level 1

Broadcast Action: The time was set. Constant Value: "android.intent.action.TIME_SET"


public static final String ACTION_TIME_TICK
Since: API Level 1

Broadcast Action: The current time has changed. Sent every minute. You can not receive this through components declared in manifests, only by exlicitly registering for it with Context.registerReceiver(). This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.TIME_TICK"
public static final String ACTION_UID_REMOVED
Since: API Level 1

Broadcast Action: A user ID has been removed from the system. The user ID number is stored in the extra data under EXTRA_UID. This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.UID_REMOVED"
public static final String ACTION_UMS_CONNECTED
Since: API Level 1

Broadcast Action: The device has entered USB Mass Storage mode. This is used mainly for the USB Settings panel. Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified when the SD card file system is mounted or unmounted Constant Value: "android.intent.action.UMS_CONNECTED"
public static final String ACTION_UMS_DISCONNECTED
Since: API Level 1

Broadcast Action: The device has exited USB Mass Storage mode. This is used mainly for the USB Settings panel. Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified when the SD card file system is mounted or unmounted Constant Value: "android.intent.action.UMS_DISCONNECTED"
public static final String ACTION_USER_PRESENT
Since: API Level 3

Broadcast Action: Sent when the user is present after device wakes up (e.g when the keyguard is gone). This is a protected intent that can only be sent by the system. Constant Value: "android.intent.action.USER_PRESENT"
public static final String ACTION_VIEW
Since: API Level 1

Activity Action: Display the data to the user. This is the most common action performed on data -- it is the generic action you can use on a piece of data to get the most reasonable thing to occur. For example, when used on a contacts entry it will view the entry; when used on a mailto: URI it will bring up a compose window filled with the information supplied by the URI; when used with a tel: URI it will invoke the dialer. Input: getData() is URI from which to retrieve data. Output: nothing.

Constant Value: "android.intent.action.VIEW"


public static final String ACTION_VOICE_COMMAND
Since: API Level 1

Activity Action: Start Voice Command. Input: Nothing. Output: Nothing. Constant Value: "android.intent.action.VOICE_COMMAND"
public static final String ACTION_WALLPAPER_CHANGED
Since: API Level 1

Broadcast Action: The current system wallpaper has changed. See WallpaperManager for retrieving the new wallpaper. Constant Value: "android.intent.action.WALLPAPER_CHANGED"
public static final String ACTION_WEB_SEARCH
Since: API Level 1

Activity Action: Perform a web search. Input: getStringExtra(SearchManager.QUERY) is the text to search for. If it is a url starts with http or https, the site will be opened. If it is plain text, Google search will be applied. Output: nothing. Constant Value: "android.intent.action.WEB_SEARCH"
public static final String CATEGORY_ALTERNATIVE
Since: API Level 1

Set if the activity should be considered as an alternative action to the data the user is currently viewing. See also CATEGORY_SELECTED_ALTERNATIVE for an alternative action that applies to the selection in a list of items. Supporting this category means that you would like your activity to be displayed in the set of alternative things the user can do, usually as part of the current activity's options menu. You will usually want to include a specific label in the <intent-filter> of this action describing to the user what it does. The action of IntentFilter with this category is important in that it describes the specific action the target will perform. This generally should not be a generic action (such as ACTION_VIEW, but rather a specific name such as "com.android.camera.action.CROP. Only one alternative of any particular action will be shown to the user, so using a specific action like this makes sure that your alternative will be displayed while also allowing other applications to provide their own overrides of that particular action. Constant Value: "android.intent.category.ALTERNATIVE"
public static final String CATEGORY_BROWSABLE
Since: API Level 1

Activities that can be safely invoked from a browser must support this category. For example, if the user is viewing a web page or an e-mail and clicks on a link in the text, the Intent generated execute that link will require the BROWSABLE category, so that only activities supporting this category will be considered as possible actions. By supporting this category, you are promising that there is nothing damaging (without user intervention) that can happen by invoking any matching Intent. Constant Value: "android.intent.category.BROWSABLE"
public static final String CATEGORY_CAR_DOCK
Since: API Level 5

An activity to run when device is inserted into a car dock. Used with ACTION_MAIN to launch an activity. For more information, see UiModeManager. Constant Value: "android.intent.category.CAR_DOCK"
public static final String CATEGORY_CAR_MODE
Since: API Level 8

Used to indicate that the activity can be used in a car environment. Constant Value: "android.intent.category.CAR_MODE"

public static final String CATEGORY_DEFAULT

Since: API Level 1

Set if the activity should be an option for the default action (center press) to perform on a piece of data. Setting this will hide from the user any activities without it set when performing an action on some data. Note that this is normal -not- set in the Intent when initiating an action -- it is for use in intent filters specified in packages. Constant Value: "android.intent.category.DEFAULT"
public static final String CATEGORY_DESK_DOCK
Since: API Level 5

An activity to run when device is inserted into a car dock. Used with ACTION_MAIN to launch an activity. For more information, see UiModeManager. Constant Value: "android.intent.category.DESK_DOCK"
public static final String CATEGORY_DEVELOPMENT_PREFERENCE
Since: API Level 1

This activity is a development preference panel. Constant Value: "android.intent.category.DEVELOPMENT_PREFERENCE"


public static final String CATEGORY_EMBED
Since: API Level 1

Capable of running inside a parent activity container. Constant Value: "android.intent.category.EMBED"


public static final String CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST
Since: API Level 1

To be used as code under test for framework instrumentation tests. Constant Value: "android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"
public static final String CATEGORY_HOME
Since: API Level 1

This is the home activity, that is the first activity that is displayed when the device boots. Constant Value: "android.intent.category.HOME"
public static final String CATEGORY_INFO
Since: API Level 3

Provides information about the package it is in; typically used if a package does not contain a CATEGORY_LAUNCHER to provide a front-door to the user without having to be shown in the all apps list. Constant Value: "android.intent.category.INFO"
public static final String CATEGORY_LAUNCHER
Since: API Level 1

Should be displayed in the top-level launcher. Constant Value: "android.intent.category.LAUNCHER"


public static final String CATEGORY_MONKEY
Since: API Level 1

This activity may be exercised by the monkey or other automated test tools. Constant Value: "android.intent.category.MONKEY"
public static final String CATEGORY_OPENABLE
Since: API Level 1

Used to indicate that a GET_CONTENT intent only wants URIs that can be opened with ContentResolver.openInputStream. Openable URIs must support the columns in OpenableColumns when queried, though it is allowable for those columns to be blank. Constant Value: "android.intent.category.OPENABLE"

public static final String CATEGORY_PREFERENCE

Since: API Level 1

This activity is a preference panel. Constant Value: "android.intent.category.PREFERENCE"


public static final String CATEGORY_SAMPLE_CODE
Since: API Level 1

To be used as an sample code example (not part of the normal user experience). Constant Value: "android.intent.category.SAMPLE_CODE"
public static final String CATEGORY_SELECTED_ALTERNATIVE
Since: API Level 1

Set if the activity should be considered as an alternative selection action to the data the user has currently selected. This is like CATEGORY_ALTERNATIVE, but is used in activities showing a list of items from which the user can select, giving them alternatives to the default action that will be performed on it. Constant Value: "android.intent.category.SELECTED_ALTERNATIVE"
public static final String CATEGORY_TAB
Since: API Level 1

Intended to be used as a tab inside of an containing TabActivity. Constant Value: "android.intent.category.TAB"


public static final String CATEGORY_TEST
Since: API Level 1

To be used as a test (not part of the normal user experience). Constant Value: "android.intent.category.TEST"
public static final String CATEGORY_UNIT_TEST
Since: API Level 1

To be used as a unit test (run through the Test Harness). Constant Value: "android.intent.category.UNIT_TEST"
public static final Creator<Intent> CREATOR public static final String EXTRA_ALARM_COUNT
Since: API Level 1

Since: API Level 1

Used as an int extra field in AlarmManager intents to tell the application being invoked how many pending alarms are being delievered with the intent. For one-shot alarms this will always be 1. For recurring alarms, this might be greater than 1 if the device was asleep or powered off at the time an earlier alarm would have been delivered. Constant Value: "android.intent.extra.ALARM_COUNT"
public static final String EXTRA_BCC
Since: API Level 1

A String[] holding e-mail addresses that should be blind carbon copied. Constant Value: "android.intent.extra.BCC"
public static final String EXTRA_CC
Since: API Level 1

A String[] holding e-mail addresses that should be carbon copied. Constant Value: "android.intent.extra.CC"
public static final String EXTRA_CHANGED_COMPONENT_NAME
Since: API Level 5

This constant is deprecated.

See EXTRA_CHANGED_COMPONENT_NAME_LIST; this field will contain only the first name in the list. Constant Value: "android.intent.extra.changed_component_name"
public static final String EXTRA_CHANGED_COMPONENT_NAME_LIST
Since: API Level 7

This field is part of ACTION_PACKAGE_CHANGED, and contains a string array of all of the components that have changed. Constant Value: "android.intent.extra.changed_component_name_list"
public static final String EXTRA_CHANGED_PACKAGE_LIST
Since: API Level 8

This field is part of ACTION_EXTERNAL_APPLICATIONS_AVAILABLE, ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE and contains a string array of all of the components that have changed. Constant Value: "android.intent.extra.changed_package_list"
public static final String EXTRA_CHANGED_UID_LIST
Since: API Level 8

This field is part of ACTION_EXTERNAL_APPLICATIONS_AVAILABLE, ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE and contains an integer array of uids of all of the components that have changed. Constant Value: "android.intent.extra.changed_uid_list"
public static final String EXTRA_DATA_REMOVED
Since: API Level 3

Used as a boolean extra field in ACTION_PACKAGE_REMOVED intents to indicate whether this represents a full uninstall (removing both the code and its data) or a partial uninstall (leaving its data, implying that this is an update). Constant Value: "android.intent.extra.DATA_REMOVED"
public static final String EXTRA_DOCK_STATE
Since: API Level 5

Used as an int extra field in ACTION_DOCK_EVENT intents to request the dock state. Possible values are EXTRA_DOCK_STATE_UNDOCKED, EXTRA_DOCK_STATE_DESK, or EXTRA_DOCK_STATE_CAR. Constant Value: "android.intent.extra.DOCK_STATE"
public static final int EXTRA_DOCK_STATE_CAR
Since: API Level 5

Used as an int value for EXTRA_DOCK_STATE to represent that the phone is in a car dock. Constant Value: 2 (0x00000002)
public static final int EXTRA_DOCK_STATE_DESK
Since: API Level 5

Used as an int value for EXTRA_DOCK_STATE to represent that the phone is in a desk dock. Constant Value: 1 (0x00000001)
public static final int EXTRA_DOCK_STATE_UNDOCKED
Since: API Level 5

Used as an int value for EXTRA_DOCK_STATE to represent that the phone is not in any dock. Constant Value: 0 (0x00000000)
public static final String EXTRA_DONT_KILL_APP
Since: API Level 1

Used as an boolean extra field in ACTION_PACKAGE_REMOVED or ACTION_PACKAGE_CHANGED intents to override the default action of restarting the application. Constant Value: "android.intent.extra.DONT_KILL_APP"
public static final String EXTRA_EMAIL
Since: API Level 1

A String[] holding e-mail addresses that should be delivered to. Constant Value: "android.intent.extra.EMAIL"
public static final String EXTRA_INITIAL_INTENTS
Since: API Level 5

A Parcelable[] of Intent or LabeledIntent objects as set with putExtra(String, Parcelable[]) of additional activities to place a the front of the list of choices, when shown to the user with a ACTION_CHOOSER. Constant Value: "android.intent.extra.INITIAL_INTENTS"
public static final String EXTRA_INTENT
Since: API Level 1

An Intent describing the choices you would like shown with ACTION_PICK_ACTIVITY. Constant Value: "android.intent.extra.INTENT"
public static final String EXTRA_KEY_EVENT
Since: API Level 1

A KeyEvent object containing the event that triggered the creation of the Intent it is in. Constant Value: "android.intent.extra.KEY_EVENT"
public static final String EXTRA_PHONE_NUMBER
Since: API Level 1

A String holding the phone number originally entered in ACTION_NEW_OUTGOING_CALL, or the actual number to call in a ACTION_CALL. Constant Value: "android.intent.extra.PHONE_NUMBER"
public static final String EXTRA_REMOTE_INTENT_TOKEN
Since: API Level 5

Used in the extra field in the remote intent. It's astring token passed with the remote intent. Constant Value: "android.intent.extra.remote_intent_token"
public static final String EXTRA_REPLACING
Since: API Level 3

Used as a boolean extra field in ACTION_PACKAGE_REMOVED intents to indicate that this is a replacement of the package, so this broadcast will immediately be followed by an add broadcast for a different version of the same package. Constant Value: "android.intent.extra.REPLACING"
public static final String EXTRA_SHORTCUT_ICON
Since: API Level 1

The name of the extra used to define the icon, as a Bitmap, of a shortcut. See Also ACTION_CREATE_SHORTCUT Constant Value: "android.intent.extra.shortcut.ICON"
public static final String EXTRA_SHORTCUT_ICON_RESOURCE
Since: API Level 1

The name of the extra used to define the icon, as a ShortcutIconResource, of a shortcut. See Also ACTION_CREATE_SHORTCUT Intent.ShortcutIconResource Constant Value: "android.intent.extra.shortcut.ICON_RESOURCE"
public static final String EXTRA_SHORTCUT_INTENT
Since: API Level 1

The name of the extra used to define the Intent of a shortcut.

See Also ACTION_CREATE_SHORTCUT Constant Value: "android.intent.extra.shortcut.INTENT"


public static final String EXTRA_SHORTCUT_NAME
Since: API Level 1

The name of the extra used to define the name of a shortcut. See Also ACTION_CREATE_SHORTCUT Constant Value: "android.intent.extra.shortcut.NAME"
public static final String EXTRA_STREAM
Since: API Level 1

A content: URI holding a stream of data associated with the Intent, used with ACTION_SEND to supply the data being sent. Constant Value: "android.intent.extra.STREAM"
public static final String EXTRA_SUBJECT
Since: API Level 1

A constant string holding the desired subject line of a message. Constant Value: "android.intent.extra.SUBJECT"
public static final String EXTRA_TEMPLATE
Since: API Level 1

The initial data to place in a newly created record. Use with ACTION_INSERT. The data here is a Map containing the same fields as would be given to the underlying ContentProvider.insert() call. Constant Value: "android.intent.extra.TEMPLATE"
public static final String EXTRA_TEXT
Since: API Level 1

A constant CharSequence that is associated with the Intent, used with ACTION_SEND to supply the literal data to be sent. Note that this may be a styled CharSequence, so you must use Bundle.getCharSequence() to retrieve it. Constant Value: "android.intent.extra.TEXT"
public static final String EXTRA_TITLE
Since: API Level 1

A CharSequence dialog title to provide to the user when used with a ACTION_CHOOSER. Constant Value: "android.intent.extra.TITLE"
public static final String EXTRA_UID
Since: API Level 1

Used as an int extra field in ACTION_UID_REMOVED intents to supply the uid the package had been assigned. Also an optional extra in ACTION_PACKAGE_REMOVED or ACTION_PACKAGE_CHANGED for the same purpose. Constant Value: "android.intent.extra.UID"
public static final int FILL_IN_ACTION
Since: API Level 1

Use with fillIn(Intent, int) to allow the current action value to be overwritten, even if it is already set. Constant Value: 1 (0x00000001)
public static final int FILL_IN_CATEGORIES
Since: API Level 1

Use with fillIn(Intent, int) to allow the current categories to be overwritten, even if they are already set. Constant Value: 4 (0x00000004)

public static final int FILL_IN_COMPONENT

Since: API Level 1

Use with fillIn(Intent, int) to allow the current component value to be overwritten, even if it is already set. Constant Value: 8 (0x00000008)
public static final int FILL_IN_DATA
Since: API Level 1

Use with fillIn(Intent, int) to allow the current data or type value overwritten, even if it is already set. Constant Value: 2 (0x00000002)
public static final int FILL_IN_PACKAGE
Since: API Level 4

Use with fillIn(Intent, int) to allow the current package value to be overwritten, even if it is already set. Constant Value: 16 (0x00000010)
public static final int FILL_IN_SOURCE_BOUNDS
Since: API Level 7

Use with fillIn(Intent, int) to allow the current package value to be overwritten, even if it is already set. Constant Value: 32 (0x00000020)
public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT
Since: API Level 1

This flag is not normally set by application code, but set for you by the system as described in the launchMode documentation for the singleTask mode. Constant Value: 4194304 (0x00400000)
public static final int FLAG_ACTIVITY_CLEAR_TOP
Since: API Level 1

If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent. For example, consider a task consisting of the activities: A, B, C, D. If D calls startActivity() with an Intent that resolves to the component of activity B, then C and D will be finished and B receive the given Intent, resulting in the stack now being: A, B. The currently running instance of activity B in the above example will either receive the new intent you are starting here in its onNewIntent() method, or be itself finished and restarted with the new intent. If it has declared its launch mode to be "multiple" (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created; for all other launch modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to the current instance's onNewIntent(). This launch mode can also be used to good effect in conjunction with FLAG_ACTIVITY_NEW_TASK: if used to start the root activity of a task, it will bring any currently running instance of that task to the foreground, and then clear it to its root state. This is especially useful, for example, when launching an activity from the notification manager. See Application Fundamentals: Activities and Tasks for more details on tasks. Constant Value: 67108864 (0x04000000)
public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
Since: API Level 3

If set, this marks a point in the task's activity stack that should be cleared when the task is reset. That is, the next time the task is brought to the foreground with FLAG_ACTIVITY_RESET_TASK_IF_NEEDED (typically as a result of the user re-launching it from home), this activity and all on top of it will be finished so that the user does not return to them, but instead returns to whatever activity preceeded it. This is useful for cases where you have a logical break in your application. For example, an e-mail application may have a command to view an attachment, which launches an image view activity to display it. This activity should be part of the e-mail application's task, since it is a part of the task the user is involved in. However, if the user leaves that task, and later selects the email app from home, we may like them to return to the conversation they were viewing, not the picture attachment, since that is

confusing. By setting this flag when launching the image viewer, that viewer and any activities it starts will be removed the next time the user returns to mail. Constant Value: 524288 (0x00080000)
public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
Since: API Level 1

If set, the new activity is not kept in the list of recently launched activities. Constant Value: 8388608 (0x00800000)
public static final int FLAG_ACTIVITY_FORWARD_RESULT
Since: API Level 1

If set and this intent is being used to launch a new activity from an existing one, then the reply target of the existing activity will be transfered to the new activity. This way the new activity can call setResult(int) and have that result sent back to the reply target of the original activity. Constant Value: 33554432 (0x02000000)
public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
Since: API Level 1

This flag is not normally set by application code, but set for you by the system if this activity is being launched from history (longpress home key). Constant Value: 1048576 (0x00100000)
public static final int FLAG_ACTIVITY_MULTIPLE_TASK
Since: API Level 1

Do not use this flag unless you are implementing your own top-level application launcher. Used in conjunction with FLAG_ACTIVITY_NEW_TASK to disable the behavior of bringing an existing task to the foreground. When set, a new task is always started to host the Activity for the Intent, regardless of whether there is already an existing task running the same thing. Because the default system does not include graphical task management, you should not use this flag unless you provide some way for a user to return back to the tasks you have launched. This flag is ignored if FLAG_ACTIVITY_NEW_TASK is not set. See Application Fundamentals: Activities and Tasks for more details on tasks. Constant Value: 134217728 (0x08000000)
public static final int FLAG_ACTIVITY_NEW_TASK
Since: API Level 1

If set, this activity will become the start of a new task on this history stack. A task (from the activity that started it to the next task activity) defines an atomic group of activities that the user can move to. Tasks can be moved to the foreground and background; all of the activities inside of a particular task always remain in the same order. See Application Fundamentals: Activities and Tasks for more details on tasks. This flag is generally used by activities that want to present a "launcher" style behavior: they give the user a list of separate things that can be done, which otherwise run completely independently of the activity launching them. When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in. See FLAG_ACTIVITY_MULTIPLE_TASK for a flag to disable this behavior. This flag can not be used when the caller is requesting a result from the activity being launched. Constant Value: 268435456 (0x10000000)
public static final int FLAG_ACTIVITY_NO_ANIMATION
Since: API Level 5

If set in an Intent passed to Context.startActivity(), this flag will prevent the system from applying an activity transition animation to go to the next activity state. This doesn't mean an animation will never run -- if another activity change happens that doesn't specify this flag before the activity started here is displayed, then that transition will be used. This this flag can be put to good use when you are going to do a series of activity operations but the animation seen by the user shouldn't be driven by the first activity change but rather a later one. Constant Value: 65536 (0x00010000)

public static final int FLAG_ACTIVITY_NO_HISTORY

Since: API Level 1

If set, the new activity is not kept in the history stack. As soon as the user navigates away from it, the activity is finished. This may also be set with the noHistory attribute. Constant Value: 1073741824 (0x40000000)
public static final int FLAG_ACTIVITY_NO_USER_ACTION
Since: API Level 3

If set, this flag will prevent the normal onUserLeaveHint() callback from occurring on the current frontmost activity before it is paused as the newly-started activity is brought to the front. Typically, an activity can rely on that callback to indicate that an explicit user action has caused their activity to be moved out of the foreground. The callback marks an appropriate point in the activity's lifecycle for it to dismiss any notifications that it intends to display "until the user has seen them," such as a blinking LED. If an activity is ever started via any non-user-driven events such as phone-call receipt or an alarm handler, this flag should be passed to Context.startActivity, ensuring that the pausing activity does not think the user has acknowledged its notification. Constant Value: 262144 (0x00040000)
public static final int FLAG_ACTIVITY_PREVIOUS_IS_TOP
Since: API Level 1

If set and this intent is being used to launch a new activity from an existing one, the current activity will not be counted as the top activity for deciding whether the new intent should be delivered to the top instead of starting a new one. The previous activity will be used as the top, with the assumption being that the current activity will finish itself immediately. Constant Value: 16777216 (0x01000000)
public static final int FLAG_ACTIVITY_REORDER_TO_FRONT
Since: API Level 3

If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought to the front of its task's history stack if it is already running. For example, consider a task consisting of four activities: A, B, C, D. If D calls startActivity() with an Intent that resolves to the component of activity B, then B will be brought to the front of the history stack, with this resulting order: A, C, D, B. This flag will be ignored if FLAG_ACTIVITY_CLEAR_TOP is also specified. Constant Value: 131072 (0x00020000)
public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
Since: API Level 1

If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed. Constant Value: 2097152 (0x00200000)
public static final int FLAG_ACTIVITY_SINGLE_TOP
Since: API Level 1

If set, the activity will not be launched if it is already running at the top of the history stack. Constant Value: 536870912 (0x20000000)
public static final int FLAG_DEBUG_LOG_RESOLUTION
Since: API Level 1

A flag you can enable for debugging: when set, log messages will be printed during the resolution of this intent to show you what has been found to create the final resolved list. Constant Value: 8 (0x00000008)
public static final int FLAG_FROM_BACKGROUND
Since: API Level 1

Can be set by the caller to indicate that this Intent is coming from a background operation, not from direct user interaction.

Constant Value: 4 (0x00000004)


public static final int FLAG_GRANT_READ_URI_PERMISSION
Since: API Level 1

If set, the recipient of this Intent will be granted permission to perform read operations on the Uri in the Intent's data. Constant Value: 1 (0x00000001)
public static final int FLAG_GRANT_WRITE_URI_PERMISSION
Since: API Level 1

If set, the recipient of this Intent will be granted permission to perform write operations on the Uri in the Intent's data. Constant Value: 2 (0x00000002)
public static final int FLAG_RECEIVER_REGISTERED_ONLY
Since: API Level 1

If set, when sending a broadcast only registered receivers will be called -- no BroadcastReceiver components will be launched. Constant Value: 1073741824 (0x40000000)
public static final int FLAG_RECEIVER_REPLACE_PENDING
Since: API Level 8

If set, when sending a broadcast the new broadcast will replace any existing pending broadcast that matches it. Matching is defined by Intent.filterEquals returning true for the intents of the two broadcasts. When a match is found, the new broadcast (and receivers associated with it) will replace the existing one in the pending broadcast list, remaining at the same position in the list. This flag is most typically used with sticky broadcasts, which only care about delivering the most recent values of the broadcast to their receivers. Constant Value: 536870912 (0x20000000)
public static final String METADATA_DOCK_HOME
Since: API Level 5

Boolean that can be supplied as meta-data with a dock activity, to indicate that the dock should take over the home key when it is active. Constant Value: "android.dock_home"
public static final int URI_INTENT_SCHEME
Since: API Level 4

Flag for use with toUri(int) and parseUri(String, int): the URI string always has the "intent:" scheme. This syntax can be used when you want to later disambiguate between URIs that are intended to describe an Intent vs. all others that should be treated as raw URIs. When used with parseUri(String, int), any other scheme will result in a generic VIEW action for that raw URI. Constant Value: 1 (0x00000001)

Public Constructors
public Intent ()
Since: API Level 1

Create an empty intent.


public Intent (Intent o)
Since: API Level 1

Copy constructor.
public Intent (String action)
Since: API Level 1

Create an intent with a given action. All other fields (data, type, class) are null. Note that the action must be in a namespace because Intents are used globally in the system -- for example the system VIEW action is android.intent.action.VIEW; an

application's custom action would be something like com.google.app.myapp.CUSTOM_ACTION. Parameters action The Intent action, such as ACTION_VIEW.
Since: API Level 1

public Intent (String action, Uri uri)

Create an intent with a given action and for a given data url. Note that the action must be in a namespace because Intents are used globally in the system -- for example the system VIEW action is android.intent.action.VIEW; an application's custom action would be something like com.google.app.myapp.CUSTOM_ACTION. Note: scheme and host name matching in the Android framework is case-sensitive, unlike the formal RFC. As a result, you should always ensure that you write your Uri with these elements using lower case letters, and normalize any Uris you receive from outside of Android to ensure the scheme and host is lower case. Parameters action uri The Intent action, such as ACTION_VIEW. The Intent data URI.
Since: API Level 1

public Intent (Context packageContext, Class<?> cls)

Create an intent for a specific component. All other fields (action, data, type, class) are null, though they can be modified later with explicit calls. This provides a convenient way to create an intent that is intended to execute a hard-coded class name, rather than relying on the system to find an appropriate class for you; see setComponent(ComponentName) for more information on the repercussions of this. Parameters packageContext cls A Context of the application package implementing this class. The component class that is to be used for the intent.

See Also setClass(Context, Class) setComponent(ComponentName) Intent(String, android.net.Uri, Context, Class)


public Intent (String action, Uri uri, Context packageContext, Class<?> cls)

Since: API Level 1

Create an intent for a specific component with a specified action and data. This is equivalent using Intent(String, android.net.Uri) to construct the Intent and then calling setClass(Context, Class) to set its class. Note: scheme and host name matching in the Android framework is case-sensitive, unlike the formal RFC. As a result, you should always ensure that you write your Uri with these elements using lower case letters, and normalize any Uris you receive from outside of Android to ensure the scheme and host is lower case. Parameters action uri packageContext cls The Intent action, such as ACTION_VIEW. The Intent data URI. A Context of the application package implementing this class. The component class that is to be used for the intent.

See Also Intent(String, android.net.Uri) Intent(Context, Class) setClass(Context, Class) setComponent(ComponentName)

Public Methods
public Intent addCategory (String category)
Since: API Level 1

Add a new category to the intent. Categories provide additional detail about the action the intent is perform. When resolving an intent, only activities that provide all of the requested categories will be used. Parameters category The desired category. This can be either one of the predefined Intent categories, or a custom category in your own namespace.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also hasCategory(String) removeCategory(String)
public Intent addFlags (int flags)

Since: API Level 1

Add additional flags to the intent (or with existing flags value). Parameters flags The new flags to set.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also setFlags(int)
public Object clone ()

Since: API Level 1

Creates and returns a copy of this Object. The default implementation returns a so-called "shallow" copy: It creates a new instance of the same class and then copies the field values (including object references) from this instance to the new instance. A "deep" copy, in contrast, would also recursively clone nested objects. A subclass that needs to implement this kind of cloning should call super.clone() to create the new instance and then create deep copies of the nested, mutable objects. Returns a copy of this object.
public Intent cloneFilter ()
Since: API Level 1

Make a clone of only the parts of the Intent that are relevant for filter matching: the action, data, type, component, and categories.
public static Intent createChooser (Intent target, CharSequence title)
Since: API Level 1

Convenience function for creating a ACTION_CHOOSER Intent. Parameters target title The Intent that the user will be selecting an activity to perform. Optional title that will be displayed in the chooser.

Returns Return a new Intent object that you can hand to Context.startActivity() and related methods.
public int describeContents ()
Since: API Level 1

Describe the kinds of special objects contained in this Parcelable's marshalled representation. Returns a bitmask indicating the set of special object types marshalled by the Parcelable.
public int fillIn (Intent other, int flags)
Since: API Level 1

Copy the contents of other in to this object, but only where fields are not defined by this object. For purposes of a field being defined, the following pieces of data in the Intent are considered to be separate fields:

action, as set by setAction(String). data URI and MIME type, as set by setData(Uri), setType(String), or setDataAndType(Uri, String). categories, as set by addCategory(String). package, as set by setPackage(String). component, as set by setComponent(ComponentName) or related methods. source bounds, as set by setSourceBounds(Rect) each top-level name in the associated extras.

In addition, you can use the FILL_IN_ACTION, FILL_IN_DATA, FILL_IN_CATEGORIES, FILL_IN_PACKAGE, and FILL_IN_COMPONENT to override the restriction where the corresponding field will not be replaced if it is already set. Note: The component field will only be copied if FILL_IN_COMPONENT is explicitly specified. For example, consider Intent A with {data="foo", categories="bar"} and Intent B with {action="gotit", data-type="some/thing", categories="one","two"}. Calling A.fillIn(B, Intent.FILL_IN_DATA) will result in A now containing: {action="gotit", data-type="some/thing", categories="bar"}. Parameters other flags Another Intent whose values are to be used to fill in the current one. Options to control which fields can be filled in.

Returns Returns a bit mask of FILL_IN_ACTION, FILL_IN_DATA, FILL_IN_CATEGORIES, FILL_IN_PACKAGE, and FILL_IN_COMPONENT indicating which fields were changed.
public boolean filterEquals (Intent other)
Since: API Level 1

Determine if two intents are the same for the purposes of intent resolution (filtering). That is, if their action, data, type, class, and categories are the same. This does not compare any extra data included in the intents. Parameters other The other Intent to compare against.

Returns Returns true if action, data, type, class, and categories are the same.
public int filterHashCode ()
Since: API Level 1

Generate hash code that matches semantics of filterEquals(). Returns Returns the hash value of the action, data, type, class, and categories. See Also filterEquals(Intent)
public String getAction ()

Since: API Level 1

Retrieve the general action to be performed, such as ACTION_VIEW. The action describes the general way the rest of the information in the intent should be interpreted -- most importantly, what to do with the data returned by getData(). Returns The action of this intent or null if none is specified. See Also setAction(String)
public boolean[] getBooleanArrayExtra (String name)

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no boolean array value was found. See Also putExtra(String, boolean[])
public boolean getBooleanExtra (String name, boolean defaultValue)

Since: API Level 1

Retrieve extended data from the intent. Parameters name defaultValue The name of the desired item. the value to be returned if no value of the desired type is stored with the given name.

Returns the value of an item that previously added with putExtra() or the default value if none was found. See Also putExtra(String, boolean)
public Bundle getBundleExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no Bundle value was found. See Also putExtra(String, Bundle)
public byte[] getByteArrayExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no byte array value was found. See Also putExtra(String, byte[])
public byte getByteExtra (String name, byte defaultValue)

Since: API Level 1

Retrieve extended data from the intent. Parameters name defaultValue The name of the desired item. the value to be returned if no value of the desired type is stored with the given name.

Returns the value of an item that previously added with putExtra() or the default value if none was found.

See Also putExtra(String, byte)


public Set<String> getCategories ()

Since: API Level 1

Return the set of all categories in the intent. If there are no categories, returns NULL. Returns Set The set of categories you can examine. Do not modify! See Also hasCategory(String) addCategory(String)
public char[] getCharArrayExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no char array value was found. See Also putExtra(String, char[])
public char getCharExtra (String name, char defaultValue)

Since: API Level 1

Retrieve extended data from the intent. Parameters name defaultValue The name of the desired item. the value to be returned if no value of the desired type is stored with the given name.

Returns the value of an item that previously added with putExtra() or the default value if none was found. See Also putExtra(String, char)
public CharSequence[] getCharSequenceArrayExtra (String name)

Since: API Level 8

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no CharSequence array value was found. See Also putExtra(String, CharSequence[])
public ArrayList<CharSequence> getCharSequenceArrayListExtra (String name)

Since: API Level 8

Retrieve extended data from the intent. Parameters name Returns The name of the desired item.

the value of an item that previously added with putExtra() or null if no ArrayList value was found. See Also putCharSequenceArrayListExtra(String, ArrayList)
public CharSequence getCharSequenceExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no CharSequence value was found. See Also putExtra(String, CharSequence)
public ComponentName getComponent ()

Since: API Level 1

Retrieve the concrete component associated with the intent. When receiving an intent, this is the component that was found to best handle it (that is, yourself) and will always be non-null; in all other cases it will be null unless explicitly set. Returns The name of the application component to handle the intent. See Also resolveActivity(PackageManager) setComponent(ComponentName)
public Uri getData ()

Since: API Level 1

Retrieve data this intent is operating on. This URI specifies the name of the data; often it uses the content: scheme, specifying data in a content provider. Other schemes may be handled by specific activities, such as http: by the web browser. Returns The URI of the data this intent is targeting or null. See Also getScheme() setData(Uri)
public String getDataString ()

Since: API Level 1

The same as getData(), but returns the URI as an encoded String.


public double[] getDoubleArrayExtra (String name)
Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no double array value was found. See Also putExtra(String, double[])
public double getDoubleExtra (String name, double defaultValue)

Since: API Level 1

Retrieve extended data from the intent. Parameters name defaultValue The name of the desired item. the value to be returned if no value of the desired type is stored with the given name.

Returns the value of an item that previously added with putExtra() or the default value if none was found. See Also putExtra(String, double)
public Bundle getExtras ()

Since: API Level 1

Retrieves a map of extended data from the intent. Returns the map of all extras previously added with putExtra(), or null if none have been added.
public int getFlags ()
Since: API Level 1

Retrieve any special flags associated with this intent. You will normally just set them with setFlags(int) and let the system take the appropriate action with them. Returns int The currently set flags. See Also setFlags(int)
public float[] getFloatArrayExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no float array value was found. See Also putExtra(String, float[])
public float getFloatExtra (String name, float defaultValue)

Since: API Level 1

Retrieve extended data from the intent. Parameters name defaultValue The name of the desired item. the value to be returned if no value of the desired type is stored with the given name.

Returns the value of an item that previously added with putExtra(), or the default value if no such item is present See Also putExtra(String, float)
public int[] getIntArrayExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters

name

The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no int array value was found. See Also putExtra(String, int[])
public int getIntExtra (String name, int defaultValue)

Since: API Level 1

Retrieve extended data from the intent. Parameters name defaultValue The name of the desired item. the value to be returned if no value of the desired type is stored with the given name.

Returns the value of an item that previously added with putExtra() or the default value if none was found. See Also putExtra(String, int)
public ArrayList<Integer> getIntegerArrayListExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no ArrayList value was found. See Also putIntegerArrayListExtra(String, ArrayList)
public static Intent getIntent (String uri)

Since: API Level 1

This method is deprecated. Use parseUri(String, int) instead. Call parseUri(String, int) with 0 flags. Throws URISyntaxException
public static Intent getIntentOld (String uri)
Since: API Level 1

Throws URISyntaxException
public long[] getLongArrayExtra (String name)
Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no long array value was found. See Also

putExtra(String, long[])
public long getLongExtra (String name, long defaultValue)

Since: API Level 1

Retrieve extended data from the intent. Parameters name defaultValue The name of the desired item. the value to be returned if no value of the desired type is stored with the given name.

Returns the value of an item that previously added with putExtra() or the default value if none was found. See Also putExtra(String, long)
public String getPackage ()

Since: API Level 4

Retrieve the application package name this Intent is limited to. When resolving an Intent, if non-null this limits the resolution to only components in the given application package. Returns The name of the application package for the Intent. See Also resolveActivity(PackageManager) setPackage(String)
public Parcelable[] getParcelableArrayExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no Parcelable[] value was found. See Also putExtra(String, Parcelable[])
public ArrayList<T> getParcelableArrayListExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no ArrayList value was found. See Also putParcelableArrayListExtra(String, ArrayList)
public T getParcelableExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name Returns The name of the desired item.

the value of an item that previously added with putExtra() or null if no Parcelable value was found. See Also putExtra(String, Parcelable)
public String getScheme ()

Since: API Level 1

Return the scheme portion of the intent's data. If the data is null or does not include a scheme, null is returned. Otherwise, the scheme prefix without the final ':' is returned, i.e. "http". This is the same as calling getData().getScheme() (and checking for null data). Returns The scheme of this intent. See Also getData()
public Serializable getSerializableExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no Serializable value was found. See Also putExtra(String, Serializable)
public short[] getShortArrayExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no short array value was found. See Also putExtra(String, short[])
public short getShortExtra (String name, short defaultValue)

Since: API Level 1

Retrieve extended data from the intent. Parameters name defaultValue The name of the desired item. the value to be returned if no value of the desired type is stored with the given name.

Returns the value of an item that previously added with putExtra() or the default value if none was found. See Also putExtra(String, short)
public Rect getSourceBounds ()

Since: API Level 7

Get the bounds of the sender of this intent, in screen coordinates. This can be used as a hint to the receiver for animations and the like. Null means that there is no source bounds.

public String[] getStringArrayExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no String array value was found. See Also putExtra(String, String[])
public ArrayList<String> getStringArrayListExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no ArrayList value was found. See Also putStringArrayListExtra(String, ArrayList)
public String getStringExtra (String name)

Since: API Level 1

Retrieve extended data from the intent. Parameters name The name of the desired item.

Returns the value of an item that previously added with putExtra() or null if no String value was found. See Also putExtra(String, String)
public String getType ()

Since: API Level 1

Retrieve any explicit MIME type included in the intent. This is usually null, as the type is determined by the intent data. Returns If a type was manually set, it is returned; else null is returned. See Also resolveType(ContentResolver) setType(String)
public boolean hasCategory (String category)

Since: API Level 1

Check if an category exists in the intent. Parameters category The category to check.

Returns boolean True if the intent contains the category, else false. See Also

getCategories() addCategory(String)
public boolean hasExtra (String name)

Since: API Level 1

Returns true if an extra value is associated with the given name. Parameters name the extra's name

Returns true if the given extra is present.


public boolean hasFileDescriptors ()
Since: API Level 1

Returns true if the Intent's extras contain a parcelled file descriptor. Returns true if the Intent contains a parcelled file descriptor.
public static Intent parseIntent (Resources resources, XmlPullParser parser, AttributeSet attrs)
Since: API Level 1

Parses the "intent" element (and its children) from XML and instantiates an Intent object. The given XML parser should be located at the tag where parsing should start (often named "intent"), from which the basic action, data, type, and package and class name will be retrieved. The function will then parse in to any child elements, looking for tags to add categories and to attach extra data to the intent. Parameters resources parser attrs The Resources to use when inflating resources. The XML parser pointing at an "intent" tag. The AttributeSet interface for retrieving extended attribute data at the current parser location.

Returns An Intent object matching the XML data. Throws XmlPullParserException IOException If there was an XML parsing error. If there was an I/O error.
Since: API Level 4

public static Intent parseUri (String uri, int flags)

Create an intent from a URI. This URI may encode the action, category, and other intent fields, if it was returned by toUri(int). If the Intent was not generate by toUri(), its data will be the entire URI and its action will be ACTION_VIEW. The URI given here must not be relative -- that is, it must include the scheme and full path. Parameters uri flags The URI to turn into an Intent. Additional processing flags. Either 0 or URI_INTENT_SCHEME.

Returns Intent The newly created Intent object. Throws URISyntaxException See Also toUri(int)
public Intent putCharSequenceArrayListExtra (String name, ArrayList<CharSequence> value)

Throws URISyntaxError if the basic URI syntax it bad (as parsed by the Uri class) or the Intent data within the URI is invalid.

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The ArrayList data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getCharSequenceArrayListExtra(String)
public Intent putExtra (String name, String[] value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The String array data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getStringArrayExtra(String)
public Intent putExtra (String name, Parcelable value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The Parcelable data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getParcelableExtra(String)
public Intent putExtra (String name, long value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The long data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement.

See Also putExtras(Intent) removeExtra(String) getLongExtra(String, long)


public Intent putExtra (String name, boolean value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The boolean data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getBooleanExtra(String, boolean)
public Intent putExtra (String name, double value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The double data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getDoubleExtra(String, double)
public Intent putExtra (String name, CharSequence[] value)

Since: API Level 8

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The CharSequence array data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getCharSequenceArrayExtra(String)
public Intent putExtra (String name, Parcelable[] value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll".

Parameters name value The name of the extra data, with package prefix. The Parcelable[] data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getParcelableArrayExtra(String)
public Intent putExtra (String name, char value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The char data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getCharExtra(String, char)
public Intent putExtra (String name, int[] value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The int array data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getIntArrayExtra(String)
public Intent putExtra (String name, int value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The integer data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent)

removeExtra(String) getIntExtra(String, int)


public Intent putExtra (String name, double[] value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The double array data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getDoubleArrayExtra(String)
public Intent putExtra (String name, float value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The float data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getFloatExtra(String, float)
public Intent putExtra (String name, short value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The short data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getShortExtra(String, short)
public Intent putExtra (String name, long[] value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters

name value

The name of the extra data, with package prefix. The byte array data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getLongArrayExtra(String)
public Intent putExtra (String name, boolean[] value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The boolean array data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getBooleanArrayExtra(String)
public Intent putExtra (String name, short[] value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The short array data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getShortArrayExtra(String)
public Intent putExtra (String name, String value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The String data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String)

getStringExtra(String)
public Intent putExtra (String name, Serializable value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The Serializable data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getSerializableExtra(String)
public Intent putExtra (String name, float[] value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The float array data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getFloatArrayExtra(String)
public Intent putExtra (String name, Bundle value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The Bundle data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getBundleExtra(String)
public Intent putExtra (String name, byte[] value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name The name of the extra data, with package prefix.

value

The byte array data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getByteArrayExtra(String)
public Intent putExtra (String name, CharSequence value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The CharSequence data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getCharSequenceExtra(String)
public Intent putExtra (String name, char[] value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The char array data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getCharArrayExtra(String)
public Intent putExtra (String name, byte value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The byte data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getByteExtra(String, byte)

public Intent putExtras (Intent src)

Since: API Level 1

Copy all extras in 'src' in to this intent. Parameters src Contains the extras to copy.

See Also putExtra(String, Bundle)


public Intent putExtras (Bundle extras)

Since: API Level 1

Add a set of extended data to the intent. The keys must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters extras The Bundle of extras to add to this intent.

See Also putExtra(String, Bundle) removeExtra(String)


public Intent putIntegerArrayListExtra (String name, ArrayList<Integer> value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The ArrayList data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getIntegerArrayListExtra(String)
public Intent putParcelableArrayListExtra (String name, ArrayList<? extends Parcelable> value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll". Parameters name value The name of the extra data, with package prefix. The ArrayList data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getParcelableArrayListExtra(String)
public Intent putStringArrayListExtra (String name, ArrayList<String> value)

Since: API Level 1

Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll".

Parameters name value The name of the extra data, with package prefix. The ArrayList data value.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also putExtras(Intent) removeExtra(String) getStringArrayListExtra(String)
public void readFromParcel (Parcel in) public void removeCategory (String category)

Since: API Level 1

Since: API Level 1

Remove an category from an intent. Parameters category The category to remove.

See Also addCategory(String)


public void removeExtra (String name)

Since: API Level 1

Remove extended data from the intent. See Also putExtra(String, Bundle)
public Intent replaceExtras (Intent src)

Since: API Level 3

Completely replace the extras in the Intent with the extras in the given Intent. Parameters src The exact extras contained in this Intent are copied into the target intent, replacing any that were previously there.
Since: API Level 3

public Intent replaceExtras (Bundle extras)

Completely replace the extras in the Intent with the given Bundle of extras. Parameters extras The new set of extras in the Intent, or null to erase all extras.
Since: API Level 1

public ComponentName resolveActivity (PackageManager pm)

Return the Activity component that should be used to handle this intent. The appropriate component is determined based on the information in the intent, evaluated as follows: If getComponent() returns an explicit class, that is returned without any further consideration. The activity must handle the CATEGORY_DEFAULT Intent category to be considered. If getAction() is non-NULL, the activity must handle this action. If resolveType(ContentResolver) returns non-NULL, the activity must handle this type. If addCategory(String) has added any categories, the activity must handle ALL of the categories specified. If getPackage() is non-NULL, only activity components in that application package will be considered. If there are no activities that satisfy all of these conditions, a null string is returned. If multiple activities are found to satisfy the intent, the one with the highest priority will be used. If there are multiple activities with

the same priority, the system will either pick the best activity based on user preference, or resolve to a system class that will allow the user to pick an activity and forward from there. This method is implemented simply by calling resolveActivity(Intent, int) with the "defaultOnly" parameter true. This API is called for you as part of starting an activity from an intent. You do not normally need to call it yourself. Parameters pm The package manager with which to resolve the Intent.

Returns Name of the component implementing an activity that can display the intent. See Also setComponent(ComponentName) getComponent() resolveActivityInfo(PackageManager, int)
public ActivityInfo resolveActivityInfo (PackageManager pm, int flags)

Since: API Level 1

Resolve the Intent into an ActivityInfo describing the activity that should execute the intent. Resolution follows the same rules as described for resolveActivity(PackageManager), but you get back the completely information about the resolved activity instead of just its class name. Parameters pm flags The package manager with which to resolve the Intent. Addition information to retrieve as per PackageManager.getActivityInfo().

Returns PackageManager.ActivityInfo See Also resolveActivity(PackageManager)


public String resolveType (ContentResolver resolver)

Since: API Level 1

Return the MIME data type of this intent. If the type field is explicitly set, that is simply returned. Otherwise, if the data is set, the type of that data is returned. If neither fields are set, a null is returned. Parameters resolver A ContentResolver that can be used to determine the MIME type of the intent's data.

Returns The MIME type of this intent. See Also getType() resolveType(Context)
public String resolveType (Context context)

Since: API Level 1

Return the MIME data type of this intent. If the type field is explicitly set, that is simply returned. Otherwise, if the data is set, the type of that data is returned. If neither fields are set, a null is returned. Returns The MIME type of this intent. See Also getType() resolveType(ContentResolver)
public String resolveTypeIfNeeded (ContentResolver resolver)

Since: API Level 1

Return the MIME data type of this intent, only if it will be needed for intent resolution. This is not generally useful for application code; it is used by the frameworks for communicating with back-end system services. Parameters resolver A ContentResolver that can be used to determine the MIME type of the intent's data.

Returns The MIME type of this intent, or null if it is unknown or not needed.
public Intent setAction (String action)
Since: API Level 1

Set the general action to be performed. Parameters action An action name, such as ACTION_VIEW. Application-specific actions should be prefixed with the vendor's package name.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also getAction()
public Intent setClass (Context packageContext, Class<?> cls)

Since: API Level 1

Convenience for calling setComponent(ComponentName) with the name returned by a Class object. Parameters packageContext cls A Context of the application package implementing this class. The class name to set, equivalent to setClassName(context, cls.getName()).

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also setComponent(ComponentName)
public Intent setClassName (String packageName, String className)

Since: API Level 1

Convenience for calling setComponent(ComponentName) with an explicit application package name and class name. Parameters packageName className The name of the package implementing the desired component. The name of a class inside of the application package that will be used as the component for this Intent.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also setComponent(ComponentName) setClass(Context, Class)
public Intent setClassName (Context packageContext, String className)

Since: API Level 1

Convenience for calling setComponent(ComponentName) with an explicit class name. Parameters packageContext className A Context of the application package implementing this class. The name of a class inside of the application package that will be used as the component for this Intent.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also setComponent(ComponentName) setClass(Context, Class)
public Intent setComponent (ComponentName component)

Since: API Level 1

(Usually optional) Explicitly set the component to handle the intent. If left with the default value of null, the system will determine the appropriate class to use based on the other fields (action, data, type, categories) in the Intent. If this class is defined, the specified class will always be used regardless of the other fields. You should only set this value when you know you absolutely want a specific class to be used; otherwise it is better to let the system find the appropriate class so that you will respect the installed applications and user preferences. Parameters component The name of the application component to handle the intent, or null to let the system find one for you.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also setClass(Context, Class) setClassName(Context, String) setClassName(String, String) getComponent() resolveActivity(PackageManager)
public Intent setData (Uri data)

Since: API Level 1

Set the data this intent is operating on. This method automatically clears any type that was previously set by setType(String). Note: scheme and host name matching in the Android framework is case-sensitive, unlike the formal RFC. As a result, you should always ensure that you write your Uri with these elements using lower case letters, and normalize any Uris you receive from outside of Android to ensure the scheme and host is lower case. Parameters data The URI of the data this intent is now targeting.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also getData() setType(String) setDataAndType(Uri, String)
public Intent setDataAndType (Uri data, String type)

Since: API Level 1

(Usually optional) Set the data for the intent along with an explicit MIME data type. This method should very rarely be used -- it allows you to override the MIME type that would ordinarily be inferred from the data with your own type given here. Note: MIME type, Uri scheme, and host name matching in the Android framework is case-sensitive, unlike the formal RFC definitions. As a result, you should always write these elements with lower case letters, and normalize any MIME types or Uris you receive from outside of Android to ensure these elements are lower case before supplying them here. Parameters data type Returns The URI of the data this intent is now targeting. The MIME type of the data being handled by this intent.

Returns the same Intent object, for chaining multiple calls into a single statement. See Also setData(Uri) setType(String)
public void setExtrasClassLoader (ClassLoader loader)

Since: API Level 1

Sets the ClassLoader that will be used when unmarshalling any Parcelable values from the extras of this Intent. Parameters loader a ClassLoader, or null to use the default loader at the time of unmarshalling.
Since: API Level 1

public Intent setFlags (int flags)

Set special flags controlling how this intent is handled. Most values here depend on the type of component being executed by the Intent, specifically the FLAG_ACTIVITY_* flags are all for use with Context.startActivity() and the FLAG_RECEIVER_* flags are all for use with Context.sendBroadcast(). See the Application Fundamentals: Activities and Tasks documentation for important information on how some of these options impact the behavior of your application. Parameters flags The desired flags.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also getFlags() addFlags(int) FLAG_GRANT_READ_URI_PERMISSION FLAG_GRANT_WRITE_URI_PERMISSION FLAG_DEBUG_LOG_RESOLUTION FLAG_FROM_BACKGROUND FLAG_ACTIVITY_BROUGHT_TO_FRONT FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET FLAG_ACTIVITY_CLEAR_TOP FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS FLAG_ACTIVITY_FORWARD_RESULT FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY FLAG_ACTIVITY_MULTIPLE_TASK FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NO_HISTORY FLAG_ACTIVITY_NO_USER_ACTION FLAG_ACTIVITY_PREVIOUS_IS_TOP FLAG_ACTIVITY_RESET_TASK_IF_NEEDED FLAG_ACTIVITY_SINGLE_TOP FLAG_RECEIVER_REGISTERED_ONLY
public Intent setPackage (String packageName)

Since: API Level 4

(Usually optional) Set an explicit application package name that limits the components this Intent will resolve to. If left to the default value of null, all components in all applications will considered. If non-null, the Intent can only match the components in the given application package. Parameters packageName Returns The name of the application package to handle the intent, or null to allow any application package.

Returns the same Intent object, for chaining multiple calls into a single statement. See Also getPackage() resolveActivity(PackageManager)
public void setSourceBounds (Rect r)

Since: API Level 7

Set the bounds of the sender of this intent, in screen coordinates. This can be used as a hint to the receiver for animations and the like. Null means that there is no source bounds.
public Intent setType (String type)
Since: API Level 1

Set an explicit MIME data type. This is used to create intents that only specify a type and not data, for example to indicate the type of data to return. This method automatically clears any data that was previously set by setData(Uri). Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC MIME types. As a result, you should always write your MIME types with lower case letters, and any MIME types you receive from outside of Android should be converted to lower case before supplying them here. Parameters type The MIME type of the data being handled by this intent.

Returns Returns the same Intent object, for chaining multiple calls into a single statement. See Also getType() setData(Uri) setDataAndType(Uri, String)
public String toString ()

Since: API Level 1

Returns a string containing a concise, human-readable description of this object. Subclasses are encouraged to override this method and provide an implementation that takes into account the object's type and data. The default implementation simply concatenates the class name, the '@' sign and a hexadecimal representation of the object's hashCode(), that is, it is equivalent to the following expression: getClass().getName() + '@' + Integer.toHexString(hashCode())

Returns a printable representation of this object.


public String toURI ()
Since: API Level 1

This method is deprecated. Use toUri(int) instead. Call toUri(int) with 0 flags.
public String toUri (int flags)
Since: API Level 4

Convert this Intent into a String holding a URI representation of it. The returned URI string has been properly URI encoded, so it can be used with Uri.parse(String). The URI contains the Intent's data as the base URI, with an additional fragment describing the action, categories, type, flags, package, component, and extras. You can convert the returned string back to an Intent with getIntent(String). Parameters flags Additional operating flags. Either 0 or URI_INTENT_SCHEME.

Returns Returns a URI encoding URI string describing the entire contents of the Intent.
public void writeToParcel (Parcel out, int flags)
Since: API Level 1

Flatten this object in to a Parcel. Parameters out flags The Parcel in which the object should be written. Additional flags about how the object should be written. May be 0 or PARCELABLE_WRITE_RETURN_VALUE.

Except as noted, this content is licensed under Apache 2.0. For details and restrictions, see the Content License. Android 2.2 r1 - 08 Sep 2010 18:21 Site Terms of Service - Privacy Policy - Brand Guidelines

ATTACHMENT F

/* * 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 com.android.phone; import import import import import import import import import import import import import import import import import import import import import import import import import import import import com.android.internal.telephony.Call; com.android.internal.telephony.CallerInfo; com.android.internal.telephony.CallerInfoAsyncQuery; com.android.internal.telephony.cdma.CdmaCallWaitingNotification; com.android.internal.telephony.cdma.SignalToneUtil; com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec; com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec; com.android.internal.telephony.Connection; com.android.internal.telephony.Phone; com.android.internal.telephony.PhoneBase; android.content.Context; android.media.AudioManager; android.media.ToneGenerator; android.os.AsyncResult; android.os.Handler; android.os.Message; android.os.SystemClock; android.os.SystemProperties; android.os.Vibrator; android.provider.CallLog; android.provider.CallLog.Calls; android.provider.Settings; android.telephony.PhoneNumberUtils; android.telephony.PhoneStateListener; android.telephony.TelephonyManager; android.text.TextUtils; android.util.EventLog; android.util.Log;

/** * Phone app module that listens for phone state changes and various other * events from the telephony layer, and triggers any resulting UI behavior * (like starting the Ringer and Incoming Call UI, playing in-call tones, * updating notifications, writing call log entries, etc.) */ public class CallNotifier extends Handler implements CallerInfoAsyncQuery.OnQueryCompleteListener { private static final String LOG_TAG = "CallNotifier"; private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); private static final boolean VDBG = (PhoneApp.DBG_LEVEL >= 2); // Maximum time we allow the CallerInfo query to run, // before giving up and falling back to the default ringtone. private static final int RINGTONE_QUERY_WAIT_TIME = 500; // msec // Timers related to CDMA Call Waiting // 1) For displaying Caller Info // 2) For disabling "Add Call" menu option once User selects Ignore or CW Timeout occures private static final int CALLWAITING_CALLERINFO_DISPLAY_TIME = 20000; // msec private static final int CALLWAITING_ADDCALL_DISABLE_TIME = 30000; // msec // Time to display the DisplayInfo Record sent by CDMA network

private static final int DISPLAYINFO_NOTIFICATION_TIME = 2000; // msec // Boolean to keep track of whether or not a CDMA Call Waiting call timed out. // // This is CDMA-specific, because with CDMA we *don't* get explicit // notification from the telephony layer that a call-waiting call has // stopped ringing. Instead, when a call-waiting call first comes in we // start a 20-second timer (see CALLWAITING_CALLERINFO_DISPLAY_DONE), and // if the timer expires we clean up the call and treat it as a missed call. // // If this field is true, that means that the current Call Waiting call // "timed out" and should be logged in Call Log as a missed call. If it's // false when we reach onCdmaCallWaitingReject(), we can assume the user // explicitly rejected this call-waiting call. // // This field is reset to false any time a call-waiting call first comes // in, and after cleaning up a missed call-waiting call. It's only ever // set to true when the CALLWAITING_CALLERINFO_DISPLAY_DONE timer fires. // // TODO: do we really need a member variable for this? Don't we always // know at the moment we call onCdmaCallWaitingReject() whether this is an // explicit rejection or not? // (Specifically: when we call onCdmaCallWaitingReject() from // PhoneUtils.hangupRingingCall() that means the user deliberately rejected // the call, and if we call onCdmaCallWaitingReject() because of a // CALLWAITING_CALLERINFO_DISPLAY_DONE event that means that it timed // out...) private boolean mCallWaitingTimeOut = false; // values used to track the query state private static final int CALLERINFO_QUERY_READY = 0; private static final int CALLERINFO_QUERYING = -1; // the state of the CallerInfo Query. private int mCallerInfoQueryState; // object used to synchronize access to mCallerInfoQueryState private Object mCallerInfoQueryStateGuard = new Object(); // Event used to indicate a query timeout. private static final int RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT = 100; // Events from private static private static private static private static private static private static private static private static private static private static private static private static the Phone final int final int final int final int final int final int final int final int final int final int final int final int object: PHONE_STATE_CHANGED = 1; PHONE_NEW_RINGING_CONNECTION = 2; PHONE_DISCONNECT = 3; PHONE_UNKNOWN_CONNECTION_APPEARED = 4; PHONE_INCOMING_RING = 5; PHONE_STATE_DISPLAYINFO = 6; PHONE_STATE_SIGNALINFO = 7; PHONE_CDMA_CALL_WAITING = 8; PHONE_ENHANCED_VP_ON = 9; PHONE_ENHANCED_VP_OFF = 10; PHONE_RINGBACK_TONE = 11; PHONE_RESEND_MUTE = 12;

// Events generated internally: private static final int PHONE_MWI_CHANGED = 21; private static final int PHONE_BATTERY_LOW = 22; private static final int CALLWAITING_CALLERINFO_DISPLAY_DONE = 23; private static final int CALLWAITING_ADDCALL_DISABLE_TIMEOUT = 24; private static final int DISPLAYINFO_NOTIFICATION_DONE = 25; private static final int EVENT_OTA_PROVISION_CHANGE = 26; private static final int CDMA_CALL_WAITING_REJECT = 27; // Emergency call related defines: private static final int EMERGENCY_TONE_OFF = 0; private static final int EMERGENCY_TONE_ALERT = 1; private static final int EMERGENCY_TONE_VIBRATE = 2; private PhoneApp mApplication; private Phone mPhone; private Ringer mRinger;

private BluetoothHandsfree mBluetoothHandsfree; private CallLogAsync mCallLog; private boolean mSilentRingerRequested; // ToneGenerator instance for playing SignalInfo tones private ToneGenerator mSignalInfoToneGenerator; // The tone volume relative to other sounds in the stream SignalInfo private static final int TONE_RELATIVE_VOLUME_SIGNALINFO = 80; private Call.State mPreviousCdmaCallState; private boolean mCdmaVoicePrivacyState = false; private boolean mIsCdmaRedialCall = false; // Emergency call tone and vibrate: private int mIsEmergencyToneOn; private int mCurrentEmergencyToneState = EMERGENCY_TONE_OFF; private EmergencyTonePlayerVibrator mEmergencyTonePlayerVibrator; // Ringback tone player private InCallTonePlayer mInCallRingbackTonePlayer; // Call waiting tone player private InCallTonePlayer mCallWaitingTonePlayer; // Cached AudioManager private AudioManager mAudioManager; public CallNotifier(PhoneApp app, Phone phone, Ringer ringer, BluetoothHandsfree btMgr, CallLogAsync callLog) { mApplication = app; mPhone = phone; mCallLog = callLog; mAudioManager = (AudioManager) mPhone.getContext().getSystemService(Context.AUDIO_SERVICE); mPhone.registerForNewRingingConnection(this, PHONE_NEW_RINGING_CONNECTION, null); mPhone.registerForPreciseCallStateChanged(this, PHONE_STATE_CHANGED, null); mPhone.registerForDisconnect(this, PHONE_DISCONNECT, null); mPhone.registerForUnknownConnection(this, PHONE_UNKNOWN_CONNECTION_APPEARED, null); mPhone.registerForIncomingRing(this, PHONE_INCOMING_RING, null); if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { mPhone.registerForCdmaOtaStatusChange(this, EVENT_OTA_PROVISION_CHANGE, null); if (DBG) log("Registering for Call Waiting, Signal and Display Info."); mPhone.registerForCallWaiting(this, PHONE_CDMA_CALL_WAITING, null); mPhone.registerForDisplayInfo(this, PHONE_STATE_DISPLAYINFO, null); mPhone.registerForSignalInfo(this, PHONE_STATE_SIGNALINFO, null); mPhone.registerForInCallVoicePrivacyOn(this, PHONE_ENHANCED_VP_ON, null); mPhone.registerForInCallVoicePrivacyOff(this, PHONE_ENHANCED_VP_OFF, null); // Instantiate the ToneGenerator for SignalInfo and CallWaiting // TODO: We probably don't need the mSignalInfoToneGenerator instance // around forever. Need to change it so as to create a ToneGenerator instance only // when a tone is being played and releases it after its done playing. try { mSignalInfoToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, TONE_RELATIVE_VOLUME_SIGNALINFO); } catch (RuntimeException e) { Log.w(LOG_TAG, "CallNotifier: Exception caught while creating " + "mSignalInfoToneGenerator: " + e); mSignalInfoToneGenerator = null; } } if (mPhone.getPhoneType() == Phone.PHONE_TYPE_GSM) { mPhone.registerForRingbackTone(this, PHONE_RINGBACK_TONE, null); mPhone.registerForResendIncallMute(this, PHONE_RESEND_MUTE, null); } mRinger = ringer; mBluetoothHandsfree = btMgr;

TelephonyManager telephonyManager = (TelephonyManager)app.getSystemService( Context.TELEPHONY_SERVICE); telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR); } @Override public void handleMessage(Message msg) { switch (msg.what) { case PHONE_NEW_RINGING_CONNECTION: if (DBG) log("RINGING... (new)"); onNewRingingConnection((AsyncResult) msg.obj); mSilentRingerRequested = false; break; case PHONE_INCOMING_RING: // repeat the ring when requested by the RIL, and when the user has NOT // specifically requested silence. if (msg.obj != null && ((AsyncResult) msg.obj).result != null) { PhoneBase pb = (PhoneBase)((AsyncResult)msg.obj).result; if ((pb.getState() == Phone.State.RINGING) && (mSilentRingerRequested == false)) { if (DBG) log("RINGING... (PHONE_INCOMING_RING event)"); mRinger.ring(); } else { if (DBG) log("RING before NEW_RING, skipping"); } } break; case PHONE_STATE_CHANGED: onPhoneStateChanged((AsyncResult) msg.obj); break; case PHONE_DISCONNECT: if (DBG) log("DISCONNECT"); onDisconnect((AsyncResult) msg.obj); break; case PHONE_UNKNOWN_CONNECTION_APPEARED: onUnknownConnectionAppeared((AsyncResult) msg.obj); break; case RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT: // CallerInfo query is taking too long! But we can't wait // any more, so start ringing NOW even if it means we won't // use the correct custom ringtone. Log.w(LOG_TAG, "CallerInfo query took too long; manually starting ringer"); // In this case we call onCustomRingQueryComplete(), just // like if the query had completed normally. (But we're // going to get the default ringtone, since we never got // the chance to call Ringer.setCustomRingtoneUri()). onCustomRingQueryComplete(); break; case PHONE_MWI_CHANGED: onMwiChanged(mPhone.getMessageWaitingIndicator()); break; case PHONE_BATTERY_LOW: onBatteryLow(); break; case PHONE_CDMA_CALL_WAITING: if (DBG) log("Received PHONE_CDMA_CALL_WAITING event"); onCdmaCallWaiting((AsyncResult) msg.obj); break; case CDMA_CALL_WAITING_REJECT:

Log.i(LOG_TAG, "Received CDMA_CALL_WAITING_REJECT event"); onCdmaCallWaitingReject(); break; case CALLWAITING_CALLERINFO_DISPLAY_DONE: Log.i(LOG_TAG, "Received CALLWAITING_CALLERINFO_DISPLAY_DONE event"); mCallWaitingTimeOut = true; onCdmaCallWaitingReject(); break; case CALLWAITING_ADDCALL_DISABLE_TIMEOUT: if (DBG) log("Received CALLWAITING_ADDCALL_DISABLE_TIMEOUT event ..."); // Set the mAddCallMenuStateAfterCW state to true mApplication.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(true); mApplication.updateInCallScreenTouchUi(); break; case PHONE_STATE_DISPLAYINFO: if (DBG) log("Received PHONE_STATE_DISPLAYINFO event"); onDisplayInfo((AsyncResult) msg.obj); break; case PHONE_STATE_SIGNALINFO: if (DBG) log("Received PHONE_STATE_SIGNALINFO event"); onSignalInfo((AsyncResult) msg.obj); break; case DISPLAYINFO_NOTIFICATION_DONE: if (DBG) log("Received Display Info notification done event ..."); CdmaDisplayInfo.dismissDisplayInfoRecord(); break; case EVENT_OTA_PROVISION_CHANGE: mApplication.handleOtaEvents(msg); break; case PHONE_ENHANCED_VP_ON: if (DBG) log("PHONE_ENHANCED_VP_ON..."); if (!mCdmaVoicePrivacyState) { int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY; new InCallTonePlayer(toneToPlay).start(); mCdmaVoicePrivacyState = true; // Update the VP icon: NotificationMgr.getDefault().updateInCallNotification(); } break; case PHONE_ENHANCED_VP_OFF: if (DBG) log("PHONE_ENHANCED_VP_OFF..."); if (mCdmaVoicePrivacyState) { int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY; new InCallTonePlayer(toneToPlay).start(); mCdmaVoicePrivacyState = false; // Update the VP icon: NotificationMgr.getDefault().updateInCallNotification(); } break; case PHONE_RINGBACK_TONE: onRingbackTone((AsyncResult) msg.obj); break; case PHONE_RESEND_MUTE: onResendMute(); break; default: // super.handleMessage(msg); } } PhoneStateListener mPhoneStateListener = new PhoneStateListener() { @Override

public void onMessageWaitingIndicatorChanged(boolean mwi) { onMwiChanged(mwi); } @Override public void onCallForwardingIndicatorChanged(boolean cfi) { onCfiChanged(cfi); } }; private void onNewRingingConnection(AsyncResult r) { Connection c = (Connection) r.result; if (DBG) log("onNewRingingConnection(): " + c); // Incoming calls are totally ignored if the device isn't provisioned yet boolean provisioned = Settings.Secure.getInt(mPhone.getContext().getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 0) != 0; if (!provisioned && !PhoneUtils.isPhoneInEcm(mPhone)) { Log.i(LOG_TAG, "CallNotifier: rejecting incoming call: not provisioned / ECM"); // Send the caller straight to voicemail, just like // "rejecting" an incoming call. PhoneUtils.hangupRingingCall(mPhone); return; } // Incoming calls are totally ignored if OTA call is active if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { boolean activateState = (mApplication.cdmaOtaScreenState.otaScreenState == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION); boolean dialogState = (mApplication.cdmaOtaScreenState.otaScreenState == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG); boolean spcState = mApplication.cdmaOtaProvisionData.inOtaSpcState; if (spcState) { Log.i(LOG_TAG, "CallNotifier: rejecting incoming call: OTA call is active"); PhoneUtils.hangupRingingCall(mPhone); return; } else if (activateState || dialogState) { if (dialogState) mApplication.dismissOtaDialogs(); mApplication.clearOtaState(); mApplication.clearInCallScreenMode(); } } if (c != null && c.isRinging()) { // Stop any signalInfo tone being played on receiving a Call if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { stopSignalInfoTone(); } Call.State state = c.getState(); // State will be either INCOMING or WAITING. if (VDBG) log("- connection is ringing! state = " + state); // if (DBG) PhoneUtils.dumpCallState(mPhone); // No need to do any service state checks here (like for // "emergency mode"), since in those states the SIM won't let // us get incoming connections in the first place. // // // // // // // // // // // // // // TODO: Consider sending out a serialized broadcast Intent here (maybe "ACTION_NEW_INCOMING_CALL"), *before* starting the ringer and going to the in-call UI. The intent should contain the caller-id info for the current connection, and say whether it would be a "call waiting" call or a regular ringing call. If anybody consumed the broadcast, we'd bail out without ringing or bringing up the in-call UI. This would give 3rd party apps a chance to listen for (and intercept) new ringing connections. An app could reject the incoming call by consuming the broadcast and doing nothing, or it could "pick up" the call (without any action by the user!) by firing off an ACTION_ANSWER intent.

// We'd need to protect this with a new "intercept incoming calls" // system permission. // Obtain a partial wake lock to make sure the CPU doesn't go to // sleep before we finish bringing up the InCallScreen. // (This will be upgraded soon to a full wake lock; see // PhoneUtils.showIncomingCallUi().) if (VDBG) log("Holding wake lock on new incoming connection."); mApplication.requestWakeState(PhoneApp.WakeState.PARTIAL); // - don't ring for call waiting connections // - do this before showing the incoming call panel if (state == Call.State.INCOMING) { PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_RINGING); startIncomingCallQuery(c); } else { if (VDBG) log("- starting call waiting tone..."); if (mCallWaitingTonePlayer == null) { mCallWaitingTonePlayer = new InCallTonePlayer(InCallTonePlayer.TONE_CALL_WAITING); mCallWaitingTonePlayer.start(); } // in this case, just fall through like before, and call // PhoneUtils.showIncomingCallUi PhoneUtils.showIncomingCallUi(); } } if (VDBG) log("- onNewRingingConnection() done."); } /** * Helper method to manage the start of incoming call queries */ private void startIncomingCallQuery(Connection c) { // TODO: cache the custom ringer object so that subsequent // calls will not need to do this query work. We can keep // the MRU ringtones in memory. We'll still need to hit // the database to get the callerinfo to act as a key, // but at least we can save the time required for the // Media player setup. The only issue with this is that // we may need to keep an eye on the resources the Media // player uses to keep these ringtones around. // make sure we're in a state where we can be ready to // query a ringtone uri. boolean shouldStartQuery = false; synchronized (mCallerInfoQueryStateGuard) { if (mCallerInfoQueryState == CALLERINFO_QUERY_READY) { mCallerInfoQueryState = CALLERINFO_QUERYING; shouldStartQuery = true; } } if (shouldStartQuery) { // create a custom ringer using the default ringer first mRinger.setCustomRingtoneUri(Settings.System.DEFAULT_RINGTONE_URI); // query the callerinfo to try to get the ringer. PhoneUtils.CallerInfoToken cit = PhoneUtils.startGetCallerInfo( mPhone.getContext(), c, this, this); // if this has already been queried then just ring, otherwise // we wait for the alloted time before ringing. if (cit.isFinal) { if (VDBG) log("- CallerInfo already up to date, using available data"); onQueryComplete(0, this, cit.currentInfo); } else { if (VDBG) log("- Starting query, posting timeout message."); sendEmptyMessageDelayed(RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT, RINGTONE_QUERY_WAIT_TIME); } // calls to PhoneUtils.showIncomingCallUi will come after the // queries are complete (or timeout). } else {

// This should never happen; its the case where an incoming call // arrives at the same time that the query is still being run, // and before the timeout window has closed. EventLog.writeEvent(EventLogTags.PHONE_UI_MULTIPLE_QUERY); // In this case, just log the request and ring. if (VDBG) log("RINGING... (request to ring arrived while query is running)"); mRinger.ring(); // in this case, just fall through like before, and call // PhoneUtils.showIncomingCallUi PhoneUtils.showIncomingCallUi(); } } /** * Performs the final steps of the onNewRingingConnection sequence: * starts the ringer, and launches the InCallScreen to show the * "incoming call" UI. * * Normally, this is called when the CallerInfo query completes (see * onQueryComplete()). In this case, onQueryComplete() has already * configured the Ringer object to use the custom ringtone (if there * is one) for this caller. So we just tell the Ringer to start, and * proceed to the InCallScreen. * * But this method can *also* be called if the * RINGTONE_QUERY_WAIT_TIME timeout expires, which means that the * CallerInfo query is taking too long. In that case, we log a * warning but otherwise we behave the same as in the normal case. * (We still tell the Ringer to start, but it's going to use the * default ringtone.) */ private void onCustomRingQueryComplete() { boolean isQueryExecutionTimeExpired = false; synchronized (mCallerInfoQueryStateGuard) { if (mCallerInfoQueryState == CALLERINFO_QUERYING) { mCallerInfoQueryState = CALLERINFO_QUERY_READY; isQueryExecutionTimeExpired = true; } } if (isQueryExecutionTimeExpired) { // There may be a problem with the query here, since the // default ringtone is playing instead of the custom one. Log.w(LOG_TAG, "CallerInfo query took too long; falling back to default ringtone"); EventLog.writeEvent(EventLogTags.PHONE_UI_RINGER_QUERY_ELAPSED); } // // // // // // // // // // // // if Make sure we still have an incoming call! (It's possible for the incoming call to have been disconnected while we were running the query. In that case we better not start the ringer here, since there won't be any future DISCONNECT event to stop it!) Note we don't have to worry about the incoming call going away *after* this check but before we call mRinger.ring() below, since in that case we *will* still get a DISCONNECT message sent to our handler. (And we will correctly stop the ringer when we process that event.) (mPhone.getState() != Phone.State.RINGING) { Log.i(LOG_TAG, "onCustomRingQueryComplete: No incoming call! Bailing out..."); // Don't start the ringer *or* bring up the "incoming call" UI. // Just bail out. return;

} // Ring, either with the queried ringtone or default one. if (VDBG) log("RINGING... (onCustomRingQueryComplete)"); mRinger.ring(); // ...and show the InCallScreen. PhoneUtils.showIncomingCallUi();

} private void onUnknownConnectionAppeared(AsyncResult r) { Phone.State state = mPhone.getState(); if (state == Phone.State.OFFHOOK) { // basically do onPhoneStateChanged + displayCallScreen onPhoneStateChanged(r); PhoneUtils.showIncomingCallUi(); } } private void onPhoneStateChanged(AsyncResult r) { Phone.State state = mPhone.getState(); if (VDBG) log("onPhoneStateChanged: state = " + state); // Turn status bar notifications on or off depending upon the state // of the phone. Notification Alerts (audible or vibrating) should // be on if and only if the phone is IDLE. NotificationMgr.getDefault().getStatusBarMgr() .enableNotificationAlerts(state == Phone.State.IDLE); if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { if ((mPhone.getForegroundCall().getState() == Call.State.ACTIVE) && ((mPreviousCdmaCallState == Call.State.DIALING) || (mPreviousCdmaCallState == Call.State.ALERTING))) { if (mIsCdmaRedialCall) { int toneToPlay = InCallTonePlayer.TONE_REDIAL; new InCallTonePlayer(toneToPlay).start(); } // Stop any signal info tone when call moves to ACTIVE state stopSignalInfoTone(); } mPreviousCdmaCallState = mPhone.getForegroundCall().getState(); } // Have the PhoneApp recompute its mShowBluetoothIndication // flag based on the (new) telephony state. // There's no need to force a UI update since we update the // in-call notification ourselves (below), and the InCallScreen // listens for phone state changes itself. mApplication.updateBluetoothIndication(false); // Update the proximity sensor mode (on devices that have a // proximity sensor). mApplication.updatePhoneState(state); if (state == Phone.State.OFFHOOK) { // stop call waiting tone if needed when answering if (mCallWaitingTonePlayer != null) { mCallWaitingTonePlayer.stopTone(); mCallWaitingTonePlayer = null; } PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_OFFHOOK); if (VDBG) log("onPhoneStateChanged: OFF HOOK"); // If Audio Mode is not In Call, then set the Audio Mode. This // changes is needed because for one of the carrier specific test case, // call is originated from the lower layer without using the UI, and // since calling does not go through DIALING state, it skips the steps // of setting the Audio Mode if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { if (mAudioManager.getMode() != AudioManager.MODE_IN_CALL) { PhoneUtils.setAudioMode(mPhone.getContext(), AudioManager.MODE_IN_CALL); } } // if the call screen is showing, let it handle the event, // otherwise handle it here. if (!mApplication.isShowingCallScreen()) { mApplication.setScreenTimeout(PhoneApp.ScreenTimeoutDuration.DEFAULT); mApplication.requestWakeState(PhoneApp.WakeState.SLEEP); }

// Since we're now in-call, the Ringer should definitely *not* // be ringing any more. (This is just a sanity-check; we // already stopped the ringer explicitly back in // PhoneUtils.answerCall(), before the call to phone.acceptCall().) // TODO: Confirm that this call really *is* unnecessary, and if so, // remove it! if (DBG) log("stopRing()... (OFFHOOK state)"); mRinger.stopRing(); // put a icon in the status bar NotificationMgr.getDefault().updateInCallNotification(); } if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { Connection c = mPhone.getForegroundCall().getLatestConnection(); if ((c != null) && (PhoneNumberUtils.isEmergencyNumber(c.getAddress()))) { if (VDBG) log("onPhoneStateChanged: it is an emergency call."); Call.State callState = mPhone.getForegroundCall().getState(); if (mEmergencyTonePlayerVibrator == null) { mEmergencyTonePlayerVibrator = new EmergencyTonePlayerVibrator(); } if (callState == Call.State.DIALING || callState == Call.State.ALERTING) { mIsEmergencyToneOn = Settings.System.getInt( mPhone.getContext().getContentResolver(), Settings.System.EMERGENCY_TONE, EMERGENCY_TONE_OFF); if (mIsEmergencyToneOn != EMERGENCY_TONE_OFF && mCurrentEmergencyToneState == EMERGENCY_TONE_OFF) { if (mEmergencyTonePlayerVibrator != null) { mEmergencyTonePlayerVibrator.start(); } } } else if (callState == Call.State.ACTIVE) { if (mCurrentEmergencyToneState != EMERGENCY_TONE_OFF) { if (mEmergencyTonePlayerVibrator != null) { mEmergencyTonePlayerVibrator.stop(); } } } } } if (mPhone.getPhoneType() == Phone.PHONE_TYPE_GSM) { Call.State callState = mPhone.getForegroundCall().getState(); if (!callState.isDialing()) { // If call get activated or disconnected before the ringback // tone stops, we have to stop it to prevent disturbing. if (mInCallRingbackTonePlayer != null) { mInCallRingbackTonePlayer.stopTone(); mInCallRingbackTonePlayer = null; } } } } void updateCallNotifierRegistrationsAfterRadioTechnologyChange() { if (DBG) Log.d(LOG_TAG, "updateCallNotifierRegistrationsAfterRadioTechnologyChange..."); // Unregister all events from the old obsolete phone mPhone.unregisterForNewRingingConnection(this); mPhone.unregisterForPreciseCallStateChanged(this); mPhone.unregisterForDisconnect(this); mPhone.unregisterForUnknownConnection(this); mPhone.unregisterForIncomingRing(this); mPhone.unregisterForCallWaiting(this); mPhone.unregisterForDisplayInfo(this); mPhone.unregisterForSignalInfo(this); mPhone.unregisterForCdmaOtaStatusChange(this); mPhone.unregisterForRingbackTone(this); mPhone.unregisterForResendIncallMute(this); // Release the ToneGenerator used for playing SignalInfo and CallWaiting if (mSignalInfoToneGenerator != null) {

mSignalInfoToneGenerator.release(); } // Clear ringback tone player mInCallRingbackTonePlayer = null; // Clear call waiting tone player mCallWaitingTonePlayer = null; mPhone.unregisterForInCallVoicePrivacyOn(this); mPhone.unregisterForInCallVoicePrivacyOff(this); // Register all events new to the new active phone mPhone.registerForNewRingingConnection(this, PHONE_NEW_RINGING_CONNECTION, null); mPhone.registerForPreciseCallStateChanged(this, PHONE_STATE_CHANGED, null); mPhone.registerForDisconnect(this, PHONE_DISCONNECT, null); mPhone.registerForUnknownConnection(this, PHONE_UNKNOWN_CONNECTION_APPEARED, null); mPhone.registerForIncomingRing(this, PHONE_INCOMING_RING, null); if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { if (DBG) log("Registering for Call Waiting, Signal and Display Info."); mPhone.registerForCallWaiting(this, PHONE_CDMA_CALL_WAITING, null); mPhone.registerForDisplayInfo(this, PHONE_STATE_DISPLAYINFO, null); mPhone.registerForSignalInfo(this, PHONE_STATE_SIGNALINFO, null); mPhone.registerForCdmaOtaStatusChange(this, EVENT_OTA_PROVISION_CHANGE, null); // Instantiate the ToneGenerator for SignalInfo try { mSignalInfoToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, TONE_RELATIVE_VOLUME_SIGNALINFO); } catch (RuntimeException e) { Log.w(LOG_TAG, "CallNotifier: Exception caught while creating " + "mSignalInfoToneGenerator: " + e); mSignalInfoToneGenerator = null; } mPhone.registerForInCallVoicePrivacyOn(this, PHONE_ENHANCED_VP_ON, null); mPhone.registerForInCallVoicePrivacyOff(this, PHONE_ENHANCED_VP_OFF, null); } if (mPhone.getPhoneType() == Phone.PHONE_TYPE_GSM) { mPhone.registerForRingbackTone(this, PHONE_RINGBACK_TONE, null); mPhone.registerForResendIncallMute(this, PHONE_RESEND_MUTE, null); } } /** * Implemented for CallerInfoAsyncQuery.OnQueryCompleteListener interface. * refreshes the CallCard data when it called. If called with this * class itself, it is assumed that we have been waiting for the ringtone * and direct to voicemail settings to update. */ public void onQueryComplete(int token, Object cookie, CallerInfo ci) { if (cookie instanceof Long) { if (VDBG) log("CallerInfo query complete, posting missed call notification"); NotificationMgr.getDefault().notifyMissedCall(ci.name, ci.phoneNumber, ci.phoneLabel, ((Long) cookie).longValue()); } else if (cookie instanceof CallNotifier) { if (VDBG) log("CallerInfo query complete, updating data"); // get rid of the timeout messages removeMessages(RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT); boolean isQueryExecutionTimeOK = false; synchronized (mCallerInfoQueryStateGuard) { if (mCallerInfoQueryState == CALLERINFO_QUERYING) { mCallerInfoQueryState = CALLERINFO_QUERY_READY; isQueryExecutionTimeOK = true; } } //if we're in the right state if (isQueryExecutionTimeOK) {

// send directly to voicemail. if (ci.shouldSendToVoicemail) { if (DBG) log("send to voicemail flag detected. hanging up."); PhoneUtils.hangupRingingCall(mPhone); return; } // set the ringtone uri to prepare for the ring. if (ci.contactRingtoneUri != null) { if (DBG) log("custom ringtone found, setting up ringer."); Ringer r = ((CallNotifier) cookie).mRinger; r.setCustomRingtoneUri(ci.contactRingtoneUri); } // ring, and other post-ring actions. onCustomRingQueryComplete(); } } } private void onDisconnect(AsyncResult r) { if (VDBG) log("onDisconnect()... phone state: " + mPhone.getState()); mCdmaVoicePrivacyState = false; int autoretrySetting = 0; if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { autoretrySetting = android.provider.Settings.System.getInt(mPhone.getContext(). getContentResolver(),android.provider.Settings.System.CALL_AUTO_RETRY, 0); } if (mPhone.getState() == Phone.State.IDLE) { PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_IDLE); } if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { // Stop any signalInfo tone being played when a call gets ended stopSignalInfoTone(); // Resetting the CdmaPhoneCallState members mApplication.cdmaPhoneCallState.resetCdmaPhoneCallState(); // Remove Call waiting timers removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE); removeMessages(CALLWAITING_ADDCALL_DISABLE_TIMEOUT); } Connection c = (Connection) r.result; if (DBG && c != null) { log("- onDisconnect: cause = " + c.getDisconnectCause() + ", incoming = " + c.isIncoming() + ", date = " + c.getCreateTime()); } // // // // // // // // // // // // if Stop the ringer if it was ringing (for an incoming call that either disconnected by itself, or was rejected by the user.) TODO: We technically *shouldn't* stop the ringer if the foreground or background call disconnects while an incoming call is still ringing, but that's a really rare corner case. It's safest to just unconditionally stop the ringer here. CDMA: For Call collision cases i.e. when the user makes an out going call and at the same time receives an Incoming Call, the Incoming Call is given higher preference. At this time framework sends a disconnect for the Out going call connection hence we should *not* be stopping the ringer being played for the Incoming Call (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { if (mPhone.getRingingCall().getState() == Call.State.INCOMING) { // Also we need to take off the "In Call" icon from the Notification // area as the Out going Call never got connected if (DBG) log("cancelCallInProgressNotification()... (onDisconnect)"); NotificationMgr.getDefault().cancelCallInProgressNotification(); } else { if (DBG) log("stopRing()... (onDisconnect)");

mRinger.stopRing(); } } else { // GSM if (DBG) log("stopRing()... (onDisconnect)"); mRinger.stopRing(); } // stop call waiting tone if needed when disconnecting if (mCallWaitingTonePlayer != null) { mCallWaitingTonePlayer.stopTone(); mCallWaitingTonePlayer = null; } // Check for the various tones we might need to play (thru the // earpiece) after a call disconnects. int toneToPlay = InCallTonePlayer.TONE_NONE; // The "Busy" or "Congestion" tone is the highest priority: if (c != null) { Connection.DisconnectCause cause = c.getDisconnectCause(); if (cause == Connection.DisconnectCause.BUSY) { if (DBG) log("- need to play BUSY tone!"); toneToPlay = InCallTonePlayer.TONE_BUSY; } else if (cause == Connection.DisconnectCause.CONGESTION) { if (DBG) log("- need to play CONGESTION tone!"); toneToPlay = InCallTonePlayer.TONE_CONGESTION; } else if (((cause == Connection.DisconnectCause.NORMAL) || (cause == Connection.DisconnectCause.LOCAL)) && (mApplication.isOtaCallInActiveState())) { if (DBG) log("- need to play OTA_CALL_END tone!"); toneToPlay = InCallTonePlayer.TONE_OTA_CALL_END; } else if (cause == Connection.DisconnectCause.CDMA_REORDER) { if (DBG) log("- need to play CDMA_REORDER tone!"); toneToPlay = InCallTonePlayer.TONE_REORDER; } else if (cause == Connection.DisconnectCause.CDMA_INTERCEPT) { if (DBG) log("- need to play CDMA_INTERCEPT tone!"); toneToPlay = InCallTonePlayer.TONE_INTERCEPT; } else if (cause == Connection.DisconnectCause.CDMA_DROP) { if (DBG) log("- need to play CDMA_DROP tone!"); toneToPlay = InCallTonePlayer.TONE_CDMA_DROP; } else if (cause == Connection.DisconnectCause.OUT_OF_SERVICE) { if (DBG) log("- need to play OUT OF SERVICE tone!"); toneToPlay = InCallTonePlayer.TONE_OUT_OF_SERVICE; } else if (cause == Connection.DisconnectCause.UNOBTAINABLE_NUMBER) { if (DBG) log("- need to play TONE_UNOBTAINABLE_NUMBER tone!"); toneToPlay = InCallTonePlayer.TONE_UNOBTAINABLE_NUMBER; } else if (cause == Connection.DisconnectCause.ERROR_UNSPECIFIED) { if (DBG) log("- DisconnectCause is ERROR_UNSPECIFIED: play TONE_CALL_ENDED!"); toneToPlay = InCallTonePlayer.TONE_CALL_ENDED; } } // // // // // // // if If we don't need to play BUSY or CONGESTION, then play the "call ended" tone if this was a "regular disconnect" (i.e. a normal call where one end or the other hung up) *and* this disconnect event caused the phone to become idle. (In other words, we *don't* play the sound if one call hangs up but there's still an active call on the other line.) TODO: We may eventually want to disable this via a preference. ((toneToPlay == InCallTonePlayer.TONE_NONE) && (mPhone.getState() == Phone.State.IDLE) && (c != null)) { Connection.DisconnectCause cause = c.getDisconnectCause(); if ((cause == Connection.DisconnectCause.NORMAL) // remote hangup || (cause == Connection.DisconnectCause.LOCAL)) { // local hangup if (VDBG) log("- need to play CALL_ENDED tone!"); toneToPlay = InCallTonePlayer.TONE_CALL_ENDED; mIsCdmaRedialCall = false; }

} if (mPhone.getState() == Phone.State.IDLE) { // Don't reset the audio mode or bluetooth/speakerphone state

// if we still need to let the user hear a tone through the earpiece. if (toneToPlay == InCallTonePlayer.TONE_NONE) { resetAudioStateAfterDisconnect(); } NotificationMgr.getDefault().cancelCallInProgressNotification(); // // // // // if If the InCallScreen is *not* in the foreground, forcibly dismiss it to make sure it won't still be in the activity history. (But if it *is* in the foreground, don't mess with it; it needs to be visible, displaying the "Call ended" state.) (!mApplication.isShowingCallScreen()) { if (VDBG) log("onDisconnect: force InCallScreen to finish()"); mApplication.dismissCallScreen();

} } if (c != null) { final String number = c.getAddress(); final long date = c.getCreateTime(); final long duration = c.getDurationMillis(); final Connection.DisconnectCause cause = c.getDisconnectCause(); // Set the "type" to be displayed in the call log (see constants in CallLog.Calls) final int callLogType; if (c.isIncoming()) { callLogType = (cause == Connection.DisconnectCause.INCOMING_MISSED ? Calls.MISSED_TYPE : Calls.INCOMING_TYPE); } else { callLogType = Calls.OUTGOING_TYPE; } if (VDBG) log("- callLogType: " + callLogType + ", UserData: " + c.getUserData());

{ final CallerInfo ci = getCallerInfoFromConnection(c); final String logNumber = getLogNumber(c, ci); // May be null.

if (DBG) log("- onDisconnect(): logNumber set to: " + logNumber); // TODO: In getLogNumber we use the presentation from // the connection for the CNAP. Should we use the one // below instead? (comes from caller info) // For international calls, 011 needs to be logged as + final int presentation = getPresentation(c, ci); if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { if ((PhoneNumberUtils.isEmergencyNumber(number)) && (mCurrentEmergencyToneState != EMERGENCY_TONE_OFF)) { if (mEmergencyTonePlayerVibrator != null) { mEmergencyTonePlayerVibrator.stop(); } } } // To prevent accidental redial of emergency numbers // (carrier requirement) the quickest solution is to // not log the emergency number. We gate on CDMA // (ugly) when we actually mean carrier X. // TODO: Clean this up and come up with a unified strategy. final boolean shouldNotlogEmergencyNumber = (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA); // Don't call isOtaSpNumber on GSM phones. final boolean isOtaNumber = (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) && mPhone.isOtaSpNumber(number); final boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(number); // Don't put OTA or CDMA Emergency calls into call log if (!(isOtaNumber || isEmergencyNumber && shouldNotlogEmergencyNumber)) { CallLogAsync.AddCallArgs args =

new CallLogAsync.AddCallArgs( mPhone.getContext(), ci, logNumber, presentation, callLogType, date, duration); mCallLog.addCall(args); } } if (callLogType == Calls.MISSED_TYPE) { // Show the "Missed call" notification. // (Note we *don't* do this if this was an incoming call that // the user deliberately rejected.) showMissedCallNotification(c, date); } // // // // if Possibly play a "post-disconnect tone" thru the earpiece. We do this here, rather than from the InCallScreen activity, since we need to do this even if you're not in the Phone UI at the moment the connection ends. (toneToPlay != InCallTonePlayer.TONE_NONE) { if (VDBG) log("- starting post-disconnect tone (" + toneToPlay + ")..."); new InCallTonePlayer(toneToPlay).start(); // // // // // // // // } if (mPhone.getState() == Phone.State.IDLE) { // Release screen wake locks if the in-call screen is not // showing. Otherwise, let the in-call screen handle this because // it needs to show the call ended screen for a couple of // seconds. if (!mApplication.isShowingCallScreen()) { if (VDBG) log("- NOT showing in-call screen; releasing wake locks!"); mApplication.setScreenTimeout(PhoneApp.ScreenTimeoutDuration.DEFAULT); mApplication.requestWakeState(PhoneApp.WakeState.SLEEP); } else { if (VDBG) log("- still showing in-call screen; not releasing wake locks."); } } else { if (VDBG) log("- phone still in use; not releasing wake locks."); } if (((mPreviousCdmaCallState == Call.State.DIALING) || (mPreviousCdmaCallState == Call.State.ALERTING)) && (!PhoneNumberUtils.isEmergencyNumber(number)) && (cause != Connection.DisconnectCause.INCOMING_MISSED ) && (cause != Connection.DisconnectCause.NORMAL) && (cause != Connection.DisconnectCause.LOCAL) && (cause != Connection.DisconnectCause.INCOMING_REJECTED)) { if (!mIsCdmaRedialCall) { if (autoretrySetting == InCallScreen.AUTO_RETRY_ON) { // TODO: (Moto): The contact reference data may need to be stored and use // here when redialing a call. For now, pass in NULL as the URI parameter. PhoneUtils.placeCall(mPhone, number, null); mIsCdmaRedialCall = true; } else { mIsCdmaRedialCall = false; } } else { mIsCdmaRedialCall = false; } } } } /** TODO: alternatively, we could start an InCallTonePlayer here with an "unlimited" tone length, and manually stop it later when this connection truly goes away. (The real connection over the network was closed as soon as we got the BUSY message. But our telephony layer keeps the connection open for a few extra seconds so we can show the "busy" indication to the user. We could stop the busy tone when *that* connection's "disconnect" event comes in.)

* Resets the audio mode and speaker state when a call ends. */ private void resetAudioStateAfterDisconnect() { if (VDBG) log("resetAudioStateAfterDisconnect()..."); if (mBluetoothHandsfree != null) { mBluetoothHandsfree.audioOff(); } // call turnOnSpeaker() with state=false and store=true even if speaker // is already off to reset user requested speaker state. PhoneUtils.turnOnSpeaker(mPhone.getContext(), false, true); PhoneUtils.setAudioMode(mPhone.getContext(), AudioManager.MODE_NORMAL); } private void onMwiChanged(boolean visible) { if (VDBG) log("onMwiChanged(): " + visible); NotificationMgr.getDefault().updateMwi(visible); } /** * Posts a delayed PHONE_MWI_CHANGED event, to schedule a "retry" for a * failed NotificationMgr.updateMwi() call. */ /* package */ void sendMwiChangedDelayed(long delayMillis) { Message message = Message.obtain(this, PHONE_MWI_CHANGED); sendMessageDelayed(message, delayMillis); } private void onCfiChanged(boolean visible) { if (VDBG) log("onCfiChanged(): " + visible); NotificationMgr.getDefault().updateCfi(visible); } /** * Indicates whether or not this ringer is ringing. */ boolean isRinging() { return mRinger.isRinging(); } /** * Stops the current ring, and tells the notifier that future * ring requests should be ignored. */ void silenceRinger() { mSilentRingerRequested = true; if (DBG) log("stopRing()... (silenceRinger)"); mRinger.stopRing(); } /** * Posts a PHONE_BATTERY_LOW event, causing us to play a warning * tone if the user is in-call. */ /* package */ void sendBatteryLow() { Message message = Message.obtain(this, PHONE_BATTERY_LOW); sendMessage(message); } private void onBatteryLow() { if (DBG) log("onBatteryLow()..."); // A "low battery" warning tone is now played by // StatusBarPolicy.updateBattery(). }

/** * Helper class to play tones through the earpiece (or speaker / BT) * during a call, using the ToneGenerator. *

* To use, just instantiate a new InCallTonePlayer * (passing in the TONE_* constant for the tone you want) * and start() it. * * When we're done playing the tone, if the phone is idle at that * point, we'll reset the audio routing and speaker state. * (That means that for tones that get played *after* a call * disconnects, like "busy" or "congestion" or "call ended", you * should NOT call resetAudioStateAfterDisconnect() yourself. * Instead, just start the InCallTonePlayer, which will automatically * defer the resetAudioStateAfterDisconnect() call until the tone * finishes playing.) */ private class InCallTonePlayer extends Thread { private int mToneId; private int mState; // The possible tones we can play. public static final int TONE_NONE = 0; public static final int TONE_CALL_WAITING = 1; public static final int TONE_BUSY = 2; public static final int TONE_CONGESTION = 3; public static final int TONE_BATTERY_LOW = 4; public static final int TONE_CALL_ENDED = 5; public static final int TONE_VOICE_PRIVACY = 6; public static final int TONE_REORDER = 7; public static final int TONE_INTERCEPT = 8; public static final int TONE_CDMA_DROP = 9; public static final int TONE_OUT_OF_SERVICE = 10; public static final int TONE_REDIAL = 11; public static final int TONE_OTA_CALL_END = 12; public static final int TONE_RING_BACK = 13; public static final int TONE_UNOBTAINABLE_NUMBER = 14; // The tone volume relative to other sounds in the stream private static final int TONE_RELATIVE_VOLUME_HIPRI = 80; private static final int TONE_RELATIVE_VOLUME_LOPRI = 50; // Buffer time (in msec) to add on to tone timeout value. // Needed mainly when the timeout value for a tone is the // exact duration of the tone itself. private static final int TONE_TIMEOUT_BUFFER = 20; // The tone state private static final int TONE_OFF = 0; private static final int TONE_ON = 1; private static final int TONE_STOPPED = 2; InCallTonePlayer(int toneId) { super(); mToneId = toneId; mState = TONE_OFF; } @Override public void run() { if (VDBG) log("InCallTonePlayer.run(toneId = " + mToneId + ")..."); int toneType = 0; // passed to ToneGenerator.startTone() int toneVolume; // passed to the ToneGenerator constructor int toneLengthMillis; switch (mToneId) { case TONE_CALL_WAITING: toneType = ToneGenerator.TONE_SUP_CALL_WAITING; toneVolume = TONE_RELATIVE_VOLUME_HIPRI; // Call waiting tone is stopped by stopTone() method toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER; break; case TONE_BUSY: int phoneType = mPhone.getPhoneType(); if (phoneType == Phone.PHONE_TYPE_CDMA) { toneType = ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT; toneVolume = TONE_RELATIVE_VOLUME_LOPRI;

toneLengthMillis = 1000; } else if (phoneType == Phone.PHONE_TYPE_GSM) { toneType = ToneGenerator.TONE_SUP_BUSY; toneVolume = TONE_RELATIVE_VOLUME_HIPRI; toneLengthMillis = 4000; } else { throw new IllegalStateException("Unexpected phone type: " + phoneType); } break; case TONE_CONGESTION: toneType = ToneGenerator.TONE_SUP_CONGESTION; toneVolume = TONE_RELATIVE_VOLUME_HIPRI; toneLengthMillis = 4000; break; case TONE_BATTERY_LOW: // For now, use ToneGenerator.TONE_PROP_ACK (two quick // beeps). TODO: is there some other ToneGenerator // tone that would be more appropriate here? Or // should we consider adding a new custom tone? toneType = ToneGenerator.TONE_PROP_ACK; toneVolume = TONE_RELATIVE_VOLUME_HIPRI; toneLengthMillis = 1000; break; case TONE_CALL_ENDED: toneType = ToneGenerator.TONE_PROP_PROMPT; toneVolume = TONE_RELATIVE_VOLUME_HIPRI; toneLengthMillis = 200; break; case TONE_OTA_CALL_END: if (mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone == OtaUtils.OTA_PLAY_SUCCESS_FAILURE_TONE_ON) { toneType = ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD; toneVolume = TONE_RELATIVE_VOLUME_HIPRI; toneLengthMillis = 750; } else { toneType = ToneGenerator.TONE_PROP_PROMPT; toneVolume = TONE_RELATIVE_VOLUME_HIPRI; toneLengthMillis = 200; } break; case TONE_VOICE_PRIVACY: toneType = ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE; toneVolume = TONE_RELATIVE_VOLUME_HIPRI; toneLengthMillis = 5000; break; case TONE_REORDER: toneType = ToneGenerator.TONE_CDMA_ABBR_REORDER; toneVolume = TONE_RELATIVE_VOLUME_LOPRI; toneLengthMillis = 4000; break; case TONE_INTERCEPT: toneType = ToneGenerator.TONE_CDMA_ABBR_INTERCEPT; toneVolume = TONE_RELATIVE_VOLUME_LOPRI; toneLengthMillis = 500; break; case TONE_CDMA_DROP: case TONE_OUT_OF_SERVICE: toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE; toneVolume = TONE_RELATIVE_VOLUME_LOPRI; toneLengthMillis = 375; break; case TONE_REDIAL: toneType = ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE; toneVolume = TONE_RELATIVE_VOLUME_LOPRI; toneLengthMillis = 5000; break; case TONE_RING_BACK: toneType = ToneGenerator.TONE_SUP_RINGTONE; toneVolume = TONE_RELATIVE_VOLUME_HIPRI; // Call ring back tone is stopped by stopTone() method toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER; break; case TONE_UNOBTAINABLE_NUMBER:

toneType = ToneGenerator.TONE_SUP_ERROR; toneVolume = TONE_RELATIVE_VOLUME_HIPRI; toneLengthMillis = 4000; break; default: throw new IllegalArgumentException("Bad toneId: " + mToneId); } // If the mToneGenerator creation fails, just continue without it. It is // a local audio signal, and is not as important. ToneGenerator toneGenerator; try { int stream; if (mBluetoothHandsfree != null) { stream = mBluetoothHandsfree.isAudioOn() ? AudioManager.STREAM_BLUETOOTH_SCO: AudioManager.STREAM_VOICE_CALL; } else { stream = AudioManager.STREAM_VOICE_CALL; } toneGenerator = new ToneGenerator(stream, toneVolume); // if (DBG) log("- created toneGenerator: " + toneGenerator); } catch (RuntimeException e) { Log.w(LOG_TAG, "InCallTonePlayer: Exception caught while creating ToneGenerator: " + e); toneGenerator = null; } // Using the ToneGenerator (with the CALL_WAITING / BUSY / // CONGESTION tones at least), the ToneGenerator itself knows // the right pattern of tones to play; we do NOT need to // manually start/stop each individual tone, or manually // insert the correct delay between tones. (We just start it // and let it run for however long we want the tone pattern to // continue.) // // TODO: When we stop the ToneGenerator in the middle of a // "tone pattern", it sounds bad if we cut if off while the // tone is actually playing. Consider adding API to the // ToneGenerator to say "stop at the next silent part of the // pattern", or simply "play the pattern N times and then // stop." boolean needToStopTone = true; boolean okToPlayTone = false; if (toneGenerator != null) { int phoneType = mPhone.getPhoneType(); int ringerMode = mAudioManager.getRingerMode(); if (phoneType == Phone.PHONE_TYPE_CDMA) { if (toneType == ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD) { if ((ringerMode != AudioManager.RINGER_MODE_SILENT) && (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) { if (DBG) log("- InCallTonePlayer: start playing call tone=" + toneType); okToPlayTone = true; needToStopTone = false; } } else if ((toneType == ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT) || (toneType == ToneGenerator.TONE_CDMA_ABBR_REORDER) || (toneType == ToneGenerator.TONE_CDMA_ABBR_INTERCEPT) || (toneType == ToneGenerator.TONE_CDMA_CALLDROP_LITE)) { if (ringerMode != AudioManager.RINGER_MODE_SILENT) { if (DBG) log("InCallTonePlayer:playing call fail tone:" + toneType); okToPlayTone = true; needToStopTone = false; } } else if ((toneType == ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE) || (toneType == ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE)) { if ((ringerMode != AudioManager.RINGER_MODE_SILENT) && (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) { if (DBG) log("InCallTonePlayer:playing tone for toneType=" + toneType); okToPlayTone = true; needToStopTone = false; } } else { // For the rest of the tones, always OK to play.

okToPlayTone = true; } } else { // Not "CDMA" okToPlayTone = true; } synchronized (this) { if (okToPlayTone && mState != TONE_STOPPED) { mState = TONE_ON; toneGenerator.startTone(toneType); try { wait(toneLengthMillis + TONE_TIMEOUT_BUFFER); } catch (InterruptedException e) { Log.w(LOG_TAG, "InCallTonePlayer stopped: " + e); } if (needToStopTone) { toneGenerator.stopTone(); } } // if (DBG) log("- InCallTonePlayer: done playing."); toneGenerator.release(); mState = TONE_OFF; } } // // // // // // // // // // // // // if } } public void stopTone() { synchronized (this) { if (mState == TONE_ON) { notify(); } mState = TONE_STOPPED; } } } /** * Displays a notification when the phone receives a DisplayInfo record. */ private void onDisplayInfo(AsyncResult r) { // Extract the DisplayInfo String from the message CdmaDisplayInfoRec displayInfoRec = (CdmaDisplayInfoRec)(r.result); if (displayInfoRec != null) { String displayInfo = displayInfoRec.alpha; if (DBG) log("onDisplayInfo: displayInfo=" + displayInfo); CdmaDisplayInfo.displayInfoRecord(mApplication, displayInfo); // start a 2 second timer sendEmptyMessageDelayed(DISPLAYINFO_NOTIFICATION_DONE, DISPLAYINFO_NOTIFICATION_TIME); } } /** Finally, do the same cleanup we otherwise would have done in onDisconnect(). (But watch out: do NOT do this if the phone is in use, since some of our tones get played *during* a call (like CALL_WAITING and BATTERY_LOW) and we definitely *don't* want to reset the audio mode / speaker / bluetooth after playing those! This call is really here for use with tones that get played *after* a call disconnects, like "busy" or "congestion" or "call ended", where the phone has already become idle but we need to defer the resetAudioStateAfterDisconnect() call till the tone finishes playing.) (mPhone.getState() == Phone.State.IDLE) { resetAudioStateAfterDisconnect();

* Helper class to play SignalInfo tones using the ToneGenerator. * * To use, just instantiate a new SignalInfoTonePlayer * (passing in the ToneID constant for the tone you want) * and start() it. */ private class SignalInfoTonePlayer extends Thread { private int mToneId; SignalInfoTonePlayer(int toneId) { super(); mToneId = toneId; } @Override public void run() { if (DBG) log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")..."); if (mSignalInfoToneGenerator != null) { //First stop any ongoing SignalInfo tone mSignalInfoToneGenerator.stopTone(); //Start playing the new tone if its a valid tone mSignalInfoToneGenerator.startTone(mToneId); } } } /** * Plays a tone when the phone receives a SignalInfo record. */ private void onSignalInfo(AsyncResult r) { if (mPhone.getRingingCall().getState() == Call.State.INCOMING) { // Do not start any new SignalInfo tone when Call state is INCOMING // and stop any previous SignalInfo tone which is being played stopSignalInfoTone(); } else { // Extract the SignalInfo String from the message CdmaSignalInfoRec signalInfoRec = (CdmaSignalInfoRec)(r.result); // Only proceed if a Signal info is present. if (signalInfoRec != null) { boolean isPresent = signalInfoRec.isPresent; if (DBG) log("onSignalInfo: isPresent=" + isPresent); if (isPresent) {// if tone is valid int uSignalType = signalInfoRec.signalType; int uAlertPitch = signalInfoRec.alertPitch; int uSignal = signalInfoRec.signal; if (DBG) log("onSignalInfo: uSignalType=" + uSignalType + ", uAlertPitch=" + uAlertPitch + ", uSignal=" + uSignal); //Map the Signal to a ToneGenerator ToneID only if Signal info is present int toneID = SignalToneUtil.getAudioToneFromSignalInfo (uSignalType, uAlertPitch, uSignal); //Create the SignalInfo tone player and pass the ToneID new SignalInfoTonePlayer(toneID).start(); } } } } /** * Stops a SignalInfo tone in the following condition * 1 - On receiving a New Ringing Call * 2 - On disconnecting a call * 3 - On answering a Call Waiting Call */ /* package */ void stopSignalInfoTone() { if (DBG) log("stopSignalInfoTone: Stopping SignalInfo tone player"); new SignalInfoTonePlayer(ToneGenerator.TONE_CDMA_SIGNAL_OFF).start(); } /**

* Plays a Call waiting tone if it is present in the second incoming call. */ private void onCdmaCallWaiting(AsyncResult r) { // Remove any previous Call waiting timers in the queue removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE); removeMessages(CALLWAITING_ADDCALL_DISABLE_TIMEOUT); // Set the Phone Call State to SINGLE_ACTIVE as there is only one connection // else we would not have received Call waiting mApplication.cdmaPhoneCallState.setCurrentCallState( CdmaPhoneCallState.PhoneCallState.SINGLE_ACTIVE); // Start the InCallScreen Activity if its not on foreground if (!mApplication.isShowingCallScreen()) { PhoneUtils.showIncomingCallUi(); } // Start timer for CW display mCallWaitingTimeOut = false; sendEmptyMessageDelayed(CALLWAITING_CALLERINFO_DISPLAY_DONE, CALLWAITING_CALLERINFO_DISPLAY_TIME); // Set the mAddCallMenuStateAfterCW state to false mApplication.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(false); // Start the timer for disabling "Add Call" menu option sendEmptyMessageDelayed(CALLWAITING_ADDCALL_DISABLE_TIMEOUT, CALLWAITING_ADDCALL_DISABLE_TIME); // Extract the Call waiting information CdmaCallWaitingNotification infoCW = (CdmaCallWaitingNotification) r.result; int isPresent = infoCW.isPresent; if (DBG) log("onCdmaCallWaiting: isPresent=" + isPresent); if (isPresent == 1 ) {//'1' if tone is valid int uSignalType = infoCW.signalType; int uAlertPitch = infoCW.alertPitch; int uSignal = infoCW.signal; if (DBG) log("onCdmaCallWaiting: uSignalType=" + uSignalType + ", uAlertPitch=" + uAlertPitch + ", uSignal=" + uSignal); //Map the Signal to a ToneGenerator ToneID only if Signal info is present int toneID = SignalToneUtil.getAudioToneFromSignalInfo(uSignalType, uAlertPitch, uSignal); //Create the SignalInfo tone player and pass the ToneID new SignalInfoTonePlayer(toneID).start(); } } /** * Posts a event causing us to clean up after rejecting (or timing-out) a * CDMA call-waiting call. * * This method is safe to call from any thread. * @see onCdmaCallWaitingReject() */ /* package */ void sendCdmaCallWaitingReject() { sendEmptyMessage(CDMA_CALL_WAITING_REJECT); } /** * Performs Call logging based on Timeout or Ignore Call Waiting Call for CDMA, * and finally calls Hangup on the Call Waiting connection. * * This method should be called only from the UI thread. * @see sendCdmaCallWaitingReject() */ private void onCdmaCallWaitingReject() { final Call ringingCall = mPhone.getRingingCall(); // Call waiting timeout scenario if (ringingCall.getState() == Call.State.WAITING) { // Code for perform Call logging and missed call notification Connection c = ringingCall.getLatestConnection();

if (c != null) { String number = c.getAddress(); int presentation = c.getNumberPresentation(); final long date = c.getCreateTime(); final long duration = c.getDurationMillis(); final int callLogType = mCallWaitingTimeOut ? Calls.MISSED_TYPE : Calls.INCOMING_TYPE; // get the callerinfo object and then log the call with it. Object o = c.getUserData(); final CallerInfo ci; if ((o == null) || (o instanceof CallerInfo)) { ci = (CallerInfo) o; } else { ci = ((PhoneUtils.CallerInfoToken) o).currentInfo; } // Do final CNAP modifications of logNumber prior to logging [mimicking // onDisconnect()] final String logNumber = PhoneUtils.modifyForSpecialCnapCases( mPhone.getContext(), ci, number, presentation); final int newPresentation = (ci != null) ? ci.numberPresentation : presentation; if (DBG) log("- onCdmaCallWaitingReject(): logNumber set to: " + logNumber + ", newPresentation value is: " + newPresentation); CallLogAsync.AddCallArgs args = new CallLogAsync.AddCallArgs( mPhone.getContext(), ci, logNumber, presentation, callLogType, date, duration); mCallLog.addCall(args); if (callLogType == Calls.MISSED_TYPE) { // Add missed call notification showMissedCallNotification(c, date); } else { // Remove Call waiting 20 second display timer in the queue removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE); } // Hangup the RingingCall connection for CW PhoneUtils.hangup(c); } //Reset the mCallWaitingTimeOut boolean mCallWaitingTimeOut = false; } } /** * Return the private variable mPreviousCdmaCallState. */ /* package */ Call.State getPreviousCdmaCallState() { return mPreviousCdmaCallState; } /** * Return the private variable mCdmaVoicePrivacyState. */ /* package */ boolean getCdmaVoicePrivacyState() { return mCdmaVoicePrivacyState; } /** * Return the private variable mIsCdmaRedialCall. */ /* package */ boolean getIsCdmaRedialCall() { return mIsCdmaRedialCall; } /** * Helper function used to show a missed call notification.

*/ private void showMissedCallNotification(Connection c, final long date) { PhoneUtils.CallerInfoToken info = PhoneUtils.startGetCallerInfo(mApplication, c, this, Long.valueOf(date)); if (info != null) { // at this point, we've requested to start a query, but it makes no // sense to log this missed call until the query comes back. if (VDBG) log("showMissedCallNotification: Querying for CallerInfo on missed call..."); if (info.isFinal) { // it seems that the query we have actually is up to date. // send the notification then. CallerInfo ci = info.currentInfo; // Check number presentation value; if we have a non-allowed presentation, // then display an appropriate presentation string instead as the missed // call. String name = ci.name; String number = ci.phoneNumber; if (ci.numberPresentation == Connection.PRESENTATION_RESTRICTED) { name = mPhone.getContext().getString(R.string.private_num); } else if (ci.numberPresentation != Connection.PRESENTATION_ALLOWED) { name = mPhone.getContext().getString(R.string.unknown); } else { number = PhoneUtils.modifyForSpecialCnapCases(mPhone.getContext(), ci, number, ci.numberPresentation); } NotificationMgr.getDefault().notifyMissedCall(name, number, ci.phoneLabel, date); } } else { // getCallerInfo() can return null in rare cases, like if we weren't // able to get a valid phone number out of the specified Connection. Log.w(LOG_TAG, "showMissedCallNotification: got null CallerInfo for Connection " + c); } } /** * Inner class to handle emergency call tone and vibrator */ private class EmergencyTonePlayerVibrator { private final int EMG_VIBRATE_LENGTH = 1000; // ms. private final int EMG_VIBRATE_PAUSE = 1000; // ms. private final long[] mVibratePattern = new long[] { EMG_VIBRATE_LENGTH, EMG_VIBRATE_PAUSE }; private ToneGenerator mToneGenerator; private Vibrator mEmgVibrator; /** * constructor */ public EmergencyTonePlayerVibrator() { } /** * Start the emergency tone or vibrator. */ private void start() { if (VDBG) log("call startEmergencyToneOrVibrate."); int ringerMode = mAudioManager.getRingerMode(); if ((mIsEmergencyToneOn == EMERGENCY_TONE_ALERT) && (ringerMode == AudioManager.RINGER_MODE_NORMAL)) { if (VDBG) log("Play Emergency Tone."); mToneGenerator = new ToneGenerator (AudioManager.STREAM_VOICE_CALL, InCallTonePlayer.TONE_RELATIVE_VOLUME_HIPRI); if (mToneGenerator != null) { mToneGenerator.startTone(ToneGenerator.TONE_CDMA_EMERGENCY_RINGBACK); mCurrentEmergencyToneState = EMERGENCY_TONE_ALERT; } } else if (mIsEmergencyToneOn == EMERGENCY_TONE_VIBRATE) { if (VDBG) log("Play Emergency Vibrate."); mEmgVibrator = new Vibrator();

if (mEmgVibrator != null) { mEmgVibrator.vibrate(mVibratePattern, 0); mCurrentEmergencyToneState = EMERGENCY_TONE_VIBRATE; } } } /** * If the emergency tone is active, stop the tone or vibrator accordingly. */ private void stop() { if (VDBG) log("call stopEmergencyToneOrVibrate."); if ((mCurrentEmergencyToneState == EMERGENCY_TONE_ALERT) && (mToneGenerator != null)) { mToneGenerator.stopTone(); mToneGenerator.release(); } else if ((mCurrentEmergencyToneState == EMERGENCY_TONE_VIBRATE) && (mEmgVibrator != null)) { mEmgVibrator.cancel(); } mCurrentEmergencyToneState = EMERGENCY_TONE_OFF; } } private void onRingbackTone(AsyncResult r) { boolean playTone = (Boolean)(r.result); if (playTone == true) { // Only play when foreground call is in DIALING or ALERTING. // to prevent a late coming playtone after ALERTING. // Don't play ringback tone if it is in play, otherwise it will cut // the current tone and replay it if (mPhone.getForegroundCall().getState().isDialing() && mInCallRingbackTonePlayer == null) { mInCallRingbackTonePlayer = new InCallTonePlayer(InCallTonePlayer.TONE_RING_BACK); mInCallRingbackTonePlayer.start(); } } else { if (mInCallRingbackTonePlayer != null) { mInCallRingbackTonePlayer.stopTone(); mInCallRingbackTonePlayer = null; } } } /** * Toggle mute and unmute requests while keeping the same mute state */ private void onResendMute() { boolean muteState = PhoneUtils.getMute(mPhone); PhoneUtils.setMuteInternal(mPhone, !muteState); PhoneUtils.setMuteInternal(mPhone, muteState); } /** * Retrieve the phone number from the caller info or the connection. * * For incoming call the number is in the Connection object. For * outgoing call we use the CallerInfo phoneNumber field if * present. All the processing should have been done already (CDMA vs GSM numbers). * * If CallerInfo is missing the phone number, get it from the connection. * Apply the Call Name Presentation (CNAP) transform in the connection on the number. * * @param conn The phone connection. * @param info The CallerInfo. Maybe null. * @return the phone number. */ private String getLogNumber(Connection conn, CallerInfo callerInfo) { String number = null; if (conn.isIncoming()) {

number = conn.getAddress(); } else { // For emergency and voicemail calls, // CallerInfo.phoneNumber does *not* contain a valid phone // number. Instead it contains an I18N'd string such as // "Emergency Number" or "Voice Mail" so we get the number // from the connection. if (null == callerInfo || TextUtils.isEmpty(callerInfo.phoneNumber) || callerInfo.isEmergencyNumber() || callerInfo.isVoiceMailNumber()) { if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { // In cdma getAddress() is not always equals to getOrigDialString(). number = conn.getOrigDialString(); } else { number = conn.getAddress(); } } else { number = callerInfo.phoneNumber; } } if (null == number) { return null; } else { int presentation = conn.getNumberPresentation(); // Do final CNAP modifications. number = PhoneUtils.modifyForSpecialCnapCases(mPhone.getContext(), callerInfo, number, presentation); number = PhoneNumberUtils.stripSeparators(number); if (VDBG) log("getLogNumber: " + number); return number; } } /** * Get the caller info. * * @param conn The phone connection. * @return The CallerInfo associated with the connection. Maybe null. */ private CallerInfo getCallerInfoFromConnection(Connection conn) { CallerInfo ci = null; Object o = conn.getUserData(); if ((o ci } else ci } return } /** * Get the presentation from the callerinfo if not null otherwise, * get it from the connection. * * @param conn The phone connection. * @param info The CallerInfo. Maybe null. * @return The presentation to use in the logs. */ private int getPresentation(Connection conn, CallerInfo callerInfo) { int presentation; if (null == callerInfo) { presentation = conn.getNumberPresentation(); } else { presentation = callerInfo.numberPresentation; if (DBG) log("- getPresentation(): ignoring connection's presentation: " + conn.getNumberPresentation()); } if (DBG) log("- getPresentation: presentation: " + presentation); return presentation; } == null) || (o instanceof CallerInfo)) { = (CallerInfo) o; { = ((PhoneUtils.CallerInfoToken) o).currentInfo; ci;

private void log(String msg) { Log.d(LOG_TAG, msg); } }

ATTACHMENT G

/* * 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 com.android.browser; import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import android.app.Activity; android.app.AlertDialog; android.app.ProgressDialog; android.app.SearchManager; android.content.ActivityNotFoundException; android.content.BroadcastReceiver; android.content.ComponentName; android.content.ContentProvider; android.content.ContentProviderClient; android.content.ContentResolver; android.content.ContentUris; android.content.ContentValues; android.content.Context; android.content.DialogInterface; android.content.Intent; android.content.IntentFilter; android.content.pm.PackageInfo; android.content.pm.PackageManager; android.content.pm.ResolveInfo; android.content.res.Configuration; android.content.res.Resources; android.database.Cursor; android.database.DatabaseUtils; android.graphics.Bitmap; android.graphics.BitmapFactory; android.graphics.Canvas; android.graphics.Picture; android.graphics.PixelFormat; android.graphics.Rect; android.graphics.drawable.Drawable; android.net.ConnectivityManager; android.net.NetworkInfo; android.net.Uri; android.net.WebAddress; android.net.http.SslCertificate; android.net.http.SslError; android.os.AsyncTask; android.os.Bundle; android.os.Debug; android.os.Environment; android.os.Handler; android.os.Message; android.os.PowerManager; android.os.Process; android.os.ServiceManager; android.os.SystemClock; android.provider.Browser; android.provider.ContactsContract; android.provider.ContactsContract.Intents.Insert; android.provider.Downloads; android.provider.MediaStore; android.speech.RecognizerResultsIntent; android.text.IClipboard; android.text.TextUtils; android.text.format.DateFormat; android.util.AttributeSet; android.util.Log; android.util.Patterns; android.view.ContextMenu; android.view.Gravity;

import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import

android.view.KeyEvent; android.view.LayoutInflater; android.view.Menu; android.view.MenuInflater; android.view.MenuItem; android.view.View; android.view.ViewGroup; android.view.Window; android.view.WindowManager; android.view.ContextMenu.ContextMenuInfo; android.view.MenuItem.OnMenuItemClickListener; android.webkit.CookieManager; android.webkit.CookieSyncManager; android.webkit.DownloadListener; android.webkit.HttpAuthHandler; android.webkit.PluginManager; android.webkit.SslErrorHandler; android.webkit.URLUtil; android.webkit.ValueCallback; android.webkit.WebChromeClient; android.webkit.WebHistoryItem; android.webkit.WebIconDatabase; android.webkit.WebView; android.widget.EditText; android.widget.FrameLayout; android.widget.LinearLayout; android.widget.TextView; android.widget.Toast; android.accounts.Account; android.accounts.AccountManager; android.accounts.AccountManagerFuture; android.accounts.AuthenticatorException; android.accounts.OperationCanceledException; android.accounts.AccountManagerCallback;

import com.android.common.Search; import com.android.common.speech.LoggingEvents; import import import import import import import import import import import import import import import import import import import java.io.ByteArrayOutputStream; java.io.File; java.io.IOException; java.io.InputStream; java.net.MalformedURLException; java.net.URI; java.net.URISyntaxException; java.net.URL; java.net.URLEncoder; java.text.ParseException; java.util.Date; java.util.HashMap; java.util.HashSet; java.util.Iterator; java.util.List; java.util.Map; java.util.Set; java.util.regex.Matcher; java.util.regex.Pattern;

public class BrowserActivity extends Activity implements View.OnCreateContextMenuListener, DownloadListener { /* Define some aliases to make these debugging flags easier to refer to. * This file imports android.provider.Browser, so we can't just refer to "Browser.DEBUG". */ private final static boolean DEBUG = com.android.browser.Browser.DEBUG; private final static boolean LOGV_ENABLED = com.android.browser.Browser.LOGV_ENABLED; private final static boolean LOGD_ENABLED = com.android.browser.Browser.LOGD_ENABLED; // These are single-character shortcuts for searching popular sources. private static final int SHORTCUT_INVALID = 0; private static final int SHORTCUT_GOOGLE_SEARCH = 1; private static final int SHORTCUT_WIKIPEDIA_SEARCH = 2; private static final int SHORTCUT_DICTIONARY_SEARCH = 3; private static final int SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH = 4; private static class ClearThumbnails extends AsyncTask<File, Void, Void> { @Override public Void doInBackground(File... files) {

if (files != null) { for (File f : files) { if (!f.delete()) { Log.e(LOGTAG, f.getPath() + " was not deleted"); } } } return null; } } /** * This layout holds everything you see below the status bar, including the * error console, the custom view container, and the webviews. */ private FrameLayout mBrowserFrameLayout; @Override public void onCreate(Bundle icicle) { if (LOGV_ENABLED) { Log.v(LOGTAG, this + " onStart"); } super.onCreate(icicle); // test the browser in OpenGL // requestWindowFeature(Window.FEATURE_OPENGL); // enable this to test the browser in 32bit if (false) { getWindow().setFormat(PixelFormat.RGBX_8888); BitmapFactory.setDefaultConfig(Bitmap.Config.ARGB_8888); } setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); mResolver = getContentResolver(); // If this was a web search request, pass it on to the default web // search provider and finish this activity. if (handleWebSearchIntent(getIntent())) { finish(); return; } mSecLockIcon = Resources.getSystem().getDrawable( android.R.drawable.ic_secure); mMixLockIcon = Resources.getSystem().getDrawable( android.R.drawable.ic_partial_secure); FrameLayout frameLayout = (FrameLayout) getWindow().getDecorView() .findViewById(com.android.internal.R.id.content); mBrowserFrameLayout = (FrameLayout) LayoutInflater.from(this) .inflate(R.layout.custom_screen, null); mContentView = (FrameLayout) mBrowserFrameLayout.findViewById( R.id.main_content); mErrorConsoleContainer = (LinearLayout) mBrowserFrameLayout .findViewById(R.id.error_console); mCustomViewContainer = (FrameLayout) mBrowserFrameLayout .findViewById(R.id.fullscreen_custom_content); frameLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS); mTitleBar = new TitleBar(this); // mTitleBar will be always shown in the fully loaded mode mTitleBar.setProgress(100); mFakeTitleBar = new TitleBar(this); // Create the tab control and our initial tab mTabControl = new TabControl(this); // Open the icon database and retain all the bookmark urls for favicons retainIconsOnStartup(); // Keep a settings instance handy. mSettings = BrowserSettings.getInstance(); mSettings.setTabControl(mTabControl); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser"); // Find out if the network is currently up.

ConnectivityManager cm = (ConnectivityManager) getSystemService( Context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); if (info != null) { mIsNetworkUp = info.isAvailable(); } /* enables registration for changes in network status from http stack */ mNetworkStateChangedFilter = new IntentFilter(); mNetworkStateChangedFilter.addAction( ConnectivityManager.CONNECTIVITY_ACTION); mNetworkStateIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals( ConnectivityManager.CONNECTIVITY_ACTION)) { NetworkInfo info = intent.getParcelableExtra( ConnectivityManager.EXTRA_NETWORK_INFO); String typeName = info.getTypeName(); String subtypeName = info.getSubtypeName(); sendNetworkType(typeName.toLowerCase(), (subtypeName != null ? subtypeName.toLowerCase() : "")); onNetworkToggle(info.isAvailable()); } } }; IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); mPackageInstallationReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); final String packageName = intent.getData() .getSchemeSpecificPart(); final boolean replacing = intent.getBooleanExtra( Intent.EXTRA_REPLACING, false); if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) { // if it is replacing, refreshPlugins() when adding return; } if (sGoogleApps.contains(packageName)) { BrowserActivity.this.packageChanged(packageName, Intent.ACTION_PACKAGE_ADDED.equals(action)); } PackageManager pm = BrowserActivity.this.getPackageManager(); PackageInfo pkgInfo = null; try { pkgInfo = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); } catch (PackageManager.NameNotFoundException e) { return; } if (pkgInfo != null) { String permissions[] = pkgInfo.requestedPermissions; if (permissions == null) { return; } boolean permissionOk = false; for (String permit : permissions) { if (PluginManager.PLUGIN_PERMISSION.equals(permit)) { permissionOk = true; break; } } if (permissionOk) { PluginManager.getInstance(BrowserActivity.this) .refreshPlugins( Intent.ACTION_PACKAGE_ADDED .equals(action)); } }

} }; registerReceiver(mPackageInstallationReceiver, filter); if (!mTabControl.restoreState(icicle)) { // clear up the thumbnail directory if we can't restore the state as // none of the files in the directory are referenced any more. new ClearThumbnails().execute( mTabControl.getThumbnailDir().listFiles()); // there is no quit on Android. But if we can't restore the state, // we can treat it as a new Browser, remove the old session cookies. CookieManager.getInstance().removeSessionCookie(); final Intent intent = getIntent(); final Bundle extra = intent.getExtras(); // Create an initial tab. // If the intent is ACTION_VIEW and data is not null, the Browser is // invoked to view the content by another application. In this case, // the tab will be close when exit. UrlData urlData = getUrlDataFromIntent(intent); String action = intent.getAction(); final Tab t = mTabControl.createNewTab( (Intent.ACTION_VIEW.equals(action) && intent.getData() != null) || RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS .equals(action), intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), urlData.mUrl); mTabControl.setCurrentTab(t); attachTabToContentView(t); WebView webView = t.getWebView(); if (extra != null) { int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0); if (scale > 0 && scale <= 1000) { webView.setInitialScale(scale); } } if (urlData.isEmpty()) { loadUrl(webView, mSettings.getHomePage()); } else { loadUrlDataIn(t, urlData); } } else { // TabControl.restoreState() will create a new tab even if // restoring the state fails. attachTabToContentView(mTabControl.getCurrentTab()); } // Read JavaScript flags if it exists. String jsFlags = mSettings.getJsFlags(); if (jsFlags.trim().length() != 0) { mTabControl.getCurrentWebView().setJsFlags(jsFlags); } // Work out which packages are installed on the system. getInstalledPackages(); // Start watching the default geolocation permissions mSystemAllowGeolocationOrigins = new SystemAllowGeolocationOrigins(getApplicationContext()); mSystemAllowGeolocationOrigins.start(); } /** * Feed the previously stored results strings to the BrowserProvider so that * the SearchDialog will show them instead of the standard searches. * @param result String to show on the editable line of the SearchDialog. */ /* package */ void showVoiceSearchResults(String result) { ContentProviderClient client = mResolver.acquireContentProviderClient( Browser.BOOKMARKS_URI); ContentProvider prov = client.getLocalContentProvider(); BrowserProvider bp = (BrowserProvider) prov; bp.setQueryResults(mTabControl.getCurrentTab().getVoiceSearchResults()); client.release(); Bundle bundle = createGoogleSearchSourceBundle( GOOGLE_SEARCH_SOURCE_SEARCHKEY); bundle.putBoolean(SearchManager.CONTEXT_IS_VOICE, true);

startSearch(result, false, bundle, false); } @Override protected void onNewIntent(Intent intent) { Tab current = mTabControl.getCurrentTab(); // When a tab is closed on exit, the current tab index is set to -1. // Reset before proceed as Browser requires the current tab to be set. if (current == null) { // Try to reset the tab in case the index was incorrect. current = mTabControl.getTab(0); if (current == null) { // No tabs at all so just ignore this intent. return; } mTabControl.setCurrentTab(current); attachTabToContentView(current); resetTitleAndIcon(current.getWebView()); } final String action = intent.getAction(); final int flags = intent.getFlags(); if (Intent.ACTION_MAIN.equals(action) || (flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { // just resume the browser return; } // In case the SearchDialog is open. ((SearchManager) getSystemService(Context.SEARCH_SERVICE)) .stopSearch(); boolean activateVoiceSearch = RecognizerResultsIntent .ACTION_VOICE_SEARCH_RESULTS.equals(action); if (Intent.ACTION_VIEW.equals(action) || Intent.ACTION_SEARCH.equals(action) || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action) || Intent.ACTION_WEB_SEARCH.equals(action) || activateVoiceSearch) { if (current.isInVoiceSearchMode()) { String title = current.getVoiceDisplayTitle(); if (title != null && title.equals(intent.getStringExtra( SearchManager.QUERY))) { // The user submitted the same search as the last voice // search, so do nothing. return; } if (Intent.ACTION_SEARCH.equals(action) && current.voiceSearchSourceIsGoogle()) { Intent logIntent = new Intent( LoggingEvents.ACTION_LOG_EVENT); logIntent.putExtra(LoggingEvents.EXTRA_EVENT, LoggingEvents.VoiceSearch.QUERY_UPDATED); logIntent.putExtra( LoggingEvents.VoiceSearch.EXTRA_QUERY_UPDATED_VALUE, intent.getDataString()); sendBroadcast(logIntent); // Note, onPageStarted will revert the voice title bar // When http://b/issue?id=2379215 is fixed, we should update // the title bar here. } } // If this was a search request (e.g. search query directly typed into the address bar), // pass it on to the default web search provider. if (handleWebSearchIntent(intent)) { return; } UrlData urlData = getUrlDataFromIntent(intent); if (urlData.isEmpty()) { urlData = new UrlData(mSettings.getHomePage()); } final String appId = intent .getStringExtra(Browser.EXTRA_APPLICATION_ID); if ((Intent.ACTION_VIEW.equals(action) // If a voice search has no appId, it means that it came // from the browser. In that case, reuse the current tab. || (activateVoiceSearch && appId != null)) && !getPackageName().equals(appId) && (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {

Tab appTab = mTabControl.getTabFromId(appId); if (appTab != null) { Log.i(LOGTAG, "Reusing tab for " + appId); // Dismiss the subwindow if applicable. dismissSubWindow(appTab); // Since we might kill the WebView, remove it from the // content view first. removeTabFromContentView(appTab); // Recreate the main WebView after destroying the old one. // If the WebView has the same original url and is on that // page, it can be reused. boolean needsLoad = mTabControl.recreateWebView(appTab, urlData); if (current != appTab) { switchToTab(mTabControl.getTabIndex(appTab)); if (needsLoad) { loadUrlDataIn(appTab, urlData); } } else { // If the tab was the current tab, we have to attach // it to the view system again. attachTabToContentView(appTab); if (needsLoad) { loadUrlDataIn(appTab, urlData); } } return; } else { // No matching application tab, try to find a regular tab // with a matching url. appTab = mTabControl.findUnusedTabWithUrl(urlData.mUrl); if (appTab != null) { if (current != appTab) { switchToTab(mTabControl.getTabIndex(appTab)); } // Otherwise, we are already viewing the correct tab. } else { // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url // will be opened in a new tab unless we have reached // MAX_TABS. Then the url will be opened in the current // tab. If a new tab is created, it will have "true" for // exit on close. openTabAndShow(urlData, true, appId); } } } else { if (!urlData.isEmpty() && urlData.mUrl.startsWith("about:debug")) { if ("about:debug.dom".equals(urlData.mUrl)) { current.getWebView().dumpDomTree(false); } else if ("about:debug.dom.file".equals(urlData.mUrl)) { current.getWebView().dumpDomTree(true); } else if ("about:debug.render".equals(urlData.mUrl)) { current.getWebView().dumpRenderTree(false); } else if ("about:debug.render.file".equals(urlData.mUrl)) { current.getWebView().dumpRenderTree(true); } else if ("about:debug.display".equals(urlData.mUrl)) { current.getWebView().dumpDisplayTree(); } else if (urlData.mUrl.startsWith("about:debug.drag")) { int index = urlData.mUrl.codePointAt(16) - '0'; if (index <= 0 || index > 9) { current.getWebView().setDragTracker(null); } else { current.getWebView().setDragTracker(new MeshTracker(index)); } } else { mSettings.toggleDebugSettings(); } return; } // Get rid of the subwindow if it exists dismissSubWindow(current); // If the current Tab is being used as an application tab, // remove the association, since the new Intent means that it is // no longer associated with that application. current.setAppId(null); loadUrlDataIn(current, urlData);

} } } private int parseUrlShortcut(String url) { if (url == null) return SHORTCUT_INVALID; // FIXME: quick search, need to be customized by setting if (url.length() > 2 && url.charAt(1) == ' ') { switch (url.charAt(0)) { case 'g': return SHORTCUT_GOOGLE_SEARCH; case 'w': return SHORTCUT_WIKIPEDIA_SEARCH; case 'd': return SHORTCUT_DICTIONARY_SEARCH; case 'l': return SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH; } } return SHORTCUT_INVALID; } /** * Launches the default web search activity with the query parameters if the given intent's data * are identified as plain search terms and not URLs/shortcuts. * @return true if the intent was handled and web search activity was launched, false if not. */ private boolean handleWebSearchIntent(Intent intent) { if (intent == null) return false; String url = null; final String action = intent.getAction(); if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS.equals( action)) { return false; } if (Intent.ACTION_VIEW.equals(action)) { Uri data = intent.getData(); if (data != null) url = data.toString(); } else if (Intent.ACTION_SEARCH.equals(action) || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action) || Intent.ACTION_WEB_SEARCH.equals(action)) { url = intent.getStringExtra(SearchManager.QUERY); } return handleWebSearchRequest(url, intent.getBundleExtra(SearchManager.APP_DATA), intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); } /** * Launches the default web search activity with the query parameters if the given url string * was identified as plain search terms and not URL/shortcut. * @return true if the request was handled and web search activity was launched, false if not. */ private boolean handleWebSearchRequest(String inUrl, Bundle appData, String extraData) { if (inUrl == null) return false; // In general, we shouldn't modify URL from Intent. // But currently, we get the user-typed URL from search box as well. String url = fixUrl(inUrl).trim(); // URLs and site specific search shortcuts are handled by the regular flow of control, so // return early. if (Patterns.WEB_URL.matcher(url).matches() || ACCEPTED_URI_SCHEMA.matcher(url).matches() || parseUrlShortcut(url) != SHORTCUT_INVALID) { return false; } final ContentResolver cr = mResolver; final String newUrl = url; new AsyncTask<Void, Void, Void>() { protected Void doInBackground(Void... unused) { Browser.updateVisitedHistory(cr, newUrl, false); Browser.addSearchUrl(cr, newUrl); return null; } }.execute(); Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.putExtra(SearchManager.QUERY, url);

if (appData != null) { intent.putExtra(SearchManager.APP_DATA, appData); } if (extraData != null) { intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData); } intent.putExtra(Browser.EXTRA_APPLICATION_ID, getPackageName()); // can't be sure there is an activity for the Intent try { startActivity(intent); } catch (ActivityNotFoundException ex) { return false; } return true; } private UrlData getUrlDataFromIntent(Intent intent) { String url = ""; Map<String, String> headers = null; if (intent != null) { final String action = intent.getAction(); if (Intent.ACTION_VIEW.equals(action)) { url = smartUrlFilter(intent.getData()); if (url != null && url.startsWith("content:")) { /* Append mimetype so webview knows how to display */ String mimeType = intent.resolveType(getContentResolver()); if (mimeType != null) { url += "?" + mimeType; } } if (url != null && url.startsWith("http")) { final Bundle pairs = intent .getBundleExtra(Browser.EXTRA_HEADERS); if (pairs != null && !pairs.isEmpty()) { Iterator<String> iter = pairs.keySet().iterator(); headers = new HashMap<String, String>(); while (iter.hasNext()) { String key = iter.next(); headers.put(key, pairs.getString(key)); } } } } else if (Intent.ACTION_SEARCH.equals(action) || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action) || Intent.ACTION_WEB_SEARCH.equals(action)) { url = intent.getStringExtra(SearchManager.QUERY); if (url != null) { mLastEnteredUrl = url; // In general, we shouldn't modify URL from Intent. // But currently, we get the user-typed URL from search box as well. url = fixUrl(url); url = smartUrlFilter(url); final ContentResolver cr = mResolver; final String newUrl = url; new AsyncTask<Void, Void, Void>() { protected Void doInBackground(Void... unused) { Browser.updateVisitedHistory(cr, newUrl, false); return null; } }.execute(); String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&"; if (url.contains(searchSource)) { String source = null; final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA); if (appData != null) { source = appData.getString(Search.SOURCE); } if (TextUtils.isEmpty(source)) { source = GOOGLE_SEARCH_SOURCE_UNKNOWN; } url = url.replace(searchSource, "&source=android-"+source+"&"); } } } } return new UrlData(url, headers, intent);

} /* package */ void showVoiceTitleBar(String title) { mTitleBar.setInVoiceMode(true); mFakeTitleBar.setInVoiceMode(true); mTitleBar.setDisplayTitle(title); mFakeTitleBar.setDisplayTitle(title); } /* package */ void revertVoiceTitleBar() { mTitleBar.setInVoiceMode(false); mFakeTitleBar.setInVoiceMode(false); mTitleBar.setDisplayTitle(mUrl); mFakeTitleBar.setDisplayTitle(mUrl); } /* package */ static String fixUrl(String inUrl) { // FIXME: Converting the url to lower case // duplicates functionality in smartUrlFilter(). // However, changing all current callers of fixUrl to // call smartUrlFilter in addition may have unwanted // consequences, and is deferred for now. int colon = inUrl.indexOf(':'); boolean allLower = true; for (int index = 0; index < colon; index++) { char ch = inUrl.charAt(index); if (!Character.isLetter(ch)) { break; } allLower &= Character.isLowerCase(ch); if (index == colon - 1 && !allLower) { inUrl = inUrl.substring(0, colon).toLowerCase() + inUrl.substring(colon); } } if (inUrl.startsWith("http://") || inUrl.startsWith("https://")) return inUrl; if (inUrl.startsWith("http:") || inUrl.startsWith("https:")) { if (inUrl.startsWith("http:/") || inUrl.startsWith("https:/")) { inUrl = inUrl.replaceFirst("/", "//"); } else inUrl = inUrl.replaceFirst(":", "://"); } return inUrl; } @Override protected void onResume() { super.onResume(); if (LOGV_ENABLED) { Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this); } if (!mActivityInPause) { Log.e(LOGTAG, "BrowserActivity is already resumed."); return; } mTabControl.resumeCurrentTab(); mActivityInPause = false; resumeWebViewTimers(); if (mWakeLock.isHeld()) { mHandler.removeMessages(RELEASE_WAKELOCK); mWakeLock.release(); } registerReceiver(mNetworkStateIntentReceiver, mNetworkStateChangedFilter); WebView.enablePlatformNotifications(); } /** * Since the actual title bar is embedded in the WebView, and removing it * would change its appearance, use a different TitleBar to show overlayed * at the top of the screen, when the menu is open or the page is loading. */ private TitleBar mFakeTitleBar;

/** * Keeps track of whether the options menu is open. This is important in * determining whether to show or hide the title bar overlay. */ private boolean mOptionsMenuOpen; /** * Only meaningful when mOptionsMenuOpen is true. This variable keeps track * of whether the configuration has changed. The first onMenuOpened call * after a configuration change is simply a reopening of the same menu * (i.e. mIconView did not change). */ private boolean mConfigChanged; /** * Whether or not the options menu is in its smaller, icon menu form. When * true, we want the title bar overlay to be up. When false, we do not. * Only meaningful if mOptionsMenuOpen is true. */ private boolean mIconView; @Override public boolean onMenuOpened(int featureId, Menu menu) { if (Window.FEATURE_OPTIONS_PANEL == featureId) { if (mOptionsMenuOpen) { if (mConfigChanged) { // We do not need to make any changes to the state of the // title bar, since the only thing that happened was a // change in orientation mConfigChanged = false; } else { if (mIconView) { // Switching the menu to expanded view, so hide the // title bar. hideFakeTitleBar(); mIconView = false; } else { // Switching the menu back to icon view, so show the // title bar once again. showFakeTitleBar(); mIconView = true; } } } else { // The options menu is closed, so open it, and show the title showFakeTitleBar(); mOptionsMenuOpen = true; mConfigChanged = false; mIconView = true; } } return true; } private void showFakeTitleBar() { if (mFakeTitleBar.getParent() == null && mActiveTabsPage == null && !mActivityInPause) { WebView mainView = mTabControl.getCurrentWebView(); // if there is no current WebView, don't show the faked title bar; if (mainView == null) { return; } WindowManager manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); // Add the title bar to the window manager so it can receive touches // while the menu is up WindowManager.LayoutParams params = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); params.gravity = Gravity.TOP; boolean atTop = mainView.getScrollY() == 0; params.windowAnimations = atTop ? 0 : R.style.TitleBar;

manager.addView(mFakeTitleBar, params); } } @Override public void onOptionsMenuClosed(Menu menu) { mOptionsMenuOpen = false; if (!mInLoad) { hideFakeTitleBar(); } else if (!mIconView) { // The page is currently loading, and we are in expanded mode, so // we were not showing the menu. Show it once again. It will be // removed when the page finishes. showFakeTitleBar(); } } private void hideFakeTitleBar() { if (mFakeTitleBar.getParent() == null) return; WindowManager.LayoutParams params = (WindowManager.LayoutParams) mFakeTitleBar.getLayoutParams(); WebView mainView = mTabControl.getCurrentWebView(); // Although we decided whether or not to animate based on the current // scroll position, the scroll position may have changed since the // fake title bar was displayed. Make sure it has the appropriate // animation/lack thereof before removing. params.windowAnimations = mainView != null && mainView.getScrollY() == 0 ? 0 : R.style.TitleBar; WindowManager manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); manager.updateViewLayout(mFakeTitleBar, params); manager.removeView(mFakeTitleBar); } /** * Special method for the fake title bar to call when displaying its context * menu, since it is in its own Window, and its parent does not show a * context menu. */ /* package */ void showTitleBarContextMenu() { if (null == mTitleBar.getParent()) { return; } openContextMenu(mTitleBar); } @Override public void onContextMenuClosed(Menu menu) { super.onContextMenuClosed(menu); if (mInLoad) { showFakeTitleBar(); } } /** * onSaveInstanceState(Bundle map) * onSaveInstanceState is called right before onStop(). The map contains * the saved state. */ @Override protected void onSaveInstanceState(Bundle outState) { if (LOGV_ENABLED) { Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this); } // the default implementation requires each view to have an id. As the // browser handles the state itself and it doesn't use id for the views, // don't call the default implementation. Otherwise it will trigger the // warning like this, "couldn't save which view has focus because the // focused view XXX has no id". // Save all the tabs mTabControl.saveState(outState); } @Override protected void onPause() { super.onPause();

if (mActivityInPause) { Log.e(LOGTAG, "BrowserActivity is already paused."); return; } mTabControl.pauseCurrentTab(); mActivityInPause = true; if (mTabControl.getCurrentIndex() >= 0 && !pauseWebViewTimers()) { mWakeLock.acquire(); mHandler.sendMessageDelayed(mHandler .obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT); } // // // // if } cancelStopToast(); // unregister network state listener unregisterReceiver(mNetworkStateIntentReceiver); WebView.disablePlatformNotifications(); } @Override protected void onDestroy() { if (LOGV_ENABLED) { Log.v(LOGTAG, "BrowserActivity.onDestroy: this=" + this); } super.onDestroy(); if (mUploadMessage != null) { mUploadMessage.onReceiveValue(null); mUploadMessage = null; } if (mTabControl == null) return; // Remove the fake title bar if it is there hideFakeTitleBar(); // Remove the current tab and sub window Tab t = mTabControl.getCurrentTab(); if (t != null) { dismissSubWindow(t); removeTabFromContentView(t); } // Destroy all the tabs mTabControl.destroy(); WebIconDatabase.getInstance().close(); unregisterReceiver(mPackageInstallationReceiver); // Stop watching the default geolocation permissions mSystemAllowGeolocationOrigins.stop(); mSystemAllowGeolocationOrigins = null; } @Override public void onConfigurationChanged(Configuration newConfig) { mConfigChanged = true; super.onConfigurationChanged(newConfig); if (mPageInfoDialog != null) { mPageInfoDialog.dismiss(); showPageInfo( mPageInfoView, mPageInfoFromShowSSLCertificateOnError); } if (mSSLCertificateDialog != null) { mSSLCertificateDialog.dismiss(); showSSLCertificate( mSSLCertificateView); } FIXME: This removes the active tabs page and resets the menu to MAIN_MENU. A better solution might be to do this work in onNewIntent but then we would need to save it in onSaveInstanceState and restore it in onCreate/onRestoreInstanceState (mActiveTabsPage != null) { removeActiveTabPage(true);

if (mSSLCertificateOnErrorDialog != null) { mSSLCertificateOnErrorDialog.dismiss(); showSSLCertificateOnError( mSSLCertificateOnErrorView, mSSLCertificateOnErrorHandler, mSSLCertificateOnErrorError); } if (mHttpAuthenticationDialog != null) { String title = ((TextView) mHttpAuthenticationDialog .findViewById(com.android.internal.R.id.alertTitle)).getText() .toString(); String name = ((TextView) mHttpAuthenticationDialog .findViewById(R.id.username_edit)).getText().toString(); String password = ((TextView) mHttpAuthenticationDialog .findViewById(R.id.password_edit)).getText().toString(); int focusId = mHttpAuthenticationDialog.getCurrentFocus() .getId(); mHttpAuthenticationDialog.dismiss(); showHttpAuthentication(mHttpAuthHandler, null, null, title, name, password, focusId); } } @Override public void onLowMemory() { super.onLowMemory(); mTabControl.freeMemory(); } private void resumeWebViewTimers() { Tab tab = mTabControl.getCurrentTab(); if (tab == null) return; // monkey can trigger this boolean inLoad = tab.inLoad(); if ((!mActivityInPause && !inLoad) || (mActivityInPause && inLoad)) { CookieSyncManager.getInstance().startSync(); WebView w = tab.getWebView(); if (w != null) { w.resumeTimers(); } } } private boolean pauseWebViewTimers() { Tab tab = mTabControl.getCurrentTab(); boolean inLoad = tab.inLoad(); if (mActivityInPause && !inLoad) { CookieSyncManager.getInstance().stopSync(); WebView w = mTabControl.getCurrentWebView(); if (w != null) { w.pauseTimers(); } return true; } else { return false; } } // Open the icon database and retain all the icons for visited sites. private void retainIconsOnStartup() { final WebIconDatabase db = WebIconDatabase.getInstance(); db.open(getDir("icons", 0).getPath()); Cursor c = null; try { c = Browser.getAllBookmarks(mResolver); if (c.moveToFirst()) { int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL); do { String url = c.getString(urlIndex); db.retainIconForPageUrl(url); } while (c.moveToNext()); } } catch (IllegalStateException e) { Log.e(LOGTAG, "retainIconsOnStartup", e); } finally { if (c!= null) c.close(); } }

// Helper method for getting the top window. WebView getTopWindow() { return mTabControl.getCurrentTopWebView(); } TabControl getTabControl() { return mTabControl; } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.browser, menu); mMenu = menu; updateInLoadMenuItems(); return true; } /** * As the menu can be open when loading state changes * we must manually update the state of the stop/reload menu * item */ private void updateInLoadMenuItems() { if (mMenu == null) { return; } MenuItem src = mInLoad ? mMenu.findItem(R.id.stop_menu_id): mMenu.findItem(R.id.reload_menu_id); MenuItem dest = mMenu.findItem(R.id.stop_reload_menu_id); dest.setIcon(src.getIcon()); dest.setTitle(src.getTitle()); } @Override public boolean onContextItemSelected(MenuItem item) { // chording is not an issue with context menus, but we use the same // options selector, so set mCanChord to true so we can access them. mCanChord = true; int id = item.getItemId(); boolean result = true; switch (id) { // For the context menu from the title bar case R.id.title_bar_copy_page_url: Tab currentTab = mTabControl.getCurrentTab(); if (null == currentTab) { result = false; break; } WebView mainView = currentTab.getWebView(); if (null == mainView) { result = false; break; } copy(mainView.getUrl()); break; // -- Browser context menu case R.id.open_context_menu_id: case R.id.open_newtab_context_menu_id: case R.id.bookmark_context_menu_id: case R.id.save_link_context_menu_id: case R.id.share_link_context_menu_id: case R.id.copy_link_context_menu_id: final WebView webView = getTopWindow(); if (null == webView) { result = false; break; } final HashMap hrefMap = new HashMap(); hrefMap.put("webview", webView); final Message msg = mHandler.obtainMessage( FOCUS_NODE_HREF, id, 0, hrefMap); webView.requestFocusNodeHref(msg); break;

default: // For other context menus result = onOptionsItemSelected(item); } mCanChord = false; return result; } private Bundle createGoogleSearchSourceBundle(String source) { Bundle bundle = new Bundle(); bundle.putString(Search.SOURCE, source); return bundle; } /* package */ void editUrl() { if (mOptionsMenuOpen) closeOptionsMenu(); String url = (getTopWindow() == null) ? null : getTopWindow().getUrl(); startSearch(mSettings.getHomePage().equals(url) ? null : url, true, null, false); } /** * Overriding this to insert a local information bundle */ @Override public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch) { if (appSearchData == null) { appSearchData = createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_TYPE); } super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch); } /** * Switch tabs. Called by the TitleBarSet when sliding the title bar * results in changing tabs. * @param index Index of the tab to change to, as defined by * mTabControl.getTabIndex(Tab t). * @return boolean True if we successfully switched to a different tab. If * the indexth tab is null, or if that tab is the same as * the current one, return false. */ /* package */ boolean switchToTab(int index) { Tab tab = mTabControl.getTab(index); Tab currentTab = mTabControl.getCurrentTab(); if (tab == null || tab == currentTab) { return false; } if (currentTab != null) { // currentTab may be null if it was just removed. In that case, // we do not need to remove it removeTabFromContentView(currentTab); } mTabControl.setCurrentTab(tab); attachTabToContentView(tab); resetTitleIconAndProgress(); updateLockIconToLatest(); return true; } /* package */ Tab openTabToHomePage() { return openTabAndShow(mSettings.getHomePage(), false, null); } /* package */ void closeCurrentWindow() { final Tab current = mTabControl.getCurrentTab(); if (mTabControl.getTabCount() == 1) { // This is the last tab. Open a new one, with the home // page and close the current one. openTabToHomePage(); closeTab(current); return; } final Tab parent = current.getParentTab(); int indexToShow = -1; if (parent != null) { indexToShow = mTabControl.getTabIndex(parent); } else {

final int currentIndex = mTabControl.getCurrentIndex(); // Try to move to the tab to the right indexToShow = currentIndex + 1; if (indexToShow > mTabControl.getTabCount() - 1) { // Try to move to the tab to the left indexToShow = currentIndex - 1; } } if (switchToTab(indexToShow)) { // Close window closeTab(current); } } private ActiveTabsPage mActiveTabsPage; /** * Remove the active tabs page. * @param needToAttach If true, the active tabs page did not attach a tab * to the content view, so we need to do that here. */ /* package */ void removeActiveTabPage(boolean needToAttach) { mContentView.removeView(mActiveTabsPage); mActiveTabsPage = null; mMenuState = R.id.MAIN_MENU; if (needToAttach) { attachTabToContentView(mTabControl.getCurrentTab()); } getTopWindow().requestFocus(); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (!mCanChord) { // The user has already fired a shortcut with this hold down of the // menu key. return false; } if (null == getTopWindow()) { return false; } if (mMenuIsDown) { // The shortcut action consumes the MENU. Even if it is still down, // it won't trigger the next shortcut action. In the case of the // shortcut action triggering a new activity, like Bookmarks, we // won't get onKeyUp for MENU. So it is important to reset it here. mMenuIsDown = false; } switch (item.getItemId()) { // -- Main menu case R.id.new_tab_menu_id: openTabToHomePage(); break; case R.id.goto_menu_id: editUrl(); break; case R.id.bookmarks_menu_id: bookmarksOrHistoryPicker(false); break; case R.id.active_tabs_menu_id: mActiveTabsPage = new ActiveTabsPage(this, mTabControl); removeTabFromContentView(mTabControl.getCurrentTab()); hideFakeTitleBar(); mContentView.addView(mActiveTabsPage, COVER_SCREEN_PARAMS); mActiveTabsPage.requestFocus(); mMenuState = EMPTY_MENU; break; case R.id.add_bookmark_menu_id: Intent i = new Intent(BrowserActivity.this, AddBookmarkPage.class); WebView w = getTopWindow(); i.putExtra("url", w.getUrl()); i.putExtra("title", w.getTitle()); i.putExtra("touch_icon_url", w.getTouchIconUrl());

i.putExtra("thumbnail", createScreenshot(w)); startActivity(i); break; case R.id.stop_reload_menu_id: if (mInLoad) { stopLoading(); } else { getTopWindow().reload(); } break; case R.id.back_menu_id: getTopWindow().goBack(); break; case R.id.forward_menu_id: getTopWindow().goForward(); break; case R.id.close_menu_id: // Close the subwindow if it exists. if (mTabControl.getCurrentSubWindow() != null) { dismissSubWindow(mTabControl.getCurrentTab()); break; } closeCurrentWindow(); break; case R.id.homepage_menu_id: Tab current = mTabControl.getCurrentTab(); if (current != null) { dismissSubWindow(current); loadUrl(current.getWebView(), mSettings.getHomePage()); } break; case R.id.preferences_menu_id: Intent intent = new Intent(this, BrowserPreferencesPage.class); intent.putExtra(BrowserPreferencesPage.CURRENT_PAGE, getTopWindow().getUrl()); startActivityForResult(intent, PREFERENCES_PAGE); break; case R.id.find_menu_id: if (null == mFindDialog) { mFindDialog = new FindDialog(this); } mFindDialog.setWebView(getTopWindow()); mFindDialog.show(); getTopWindow().setFindIsUp(true); mMenuState = EMPTY_MENU; break; case R.id.select_text_id: getTopWindow().emulateShiftHeld(); break; case R.id.page_info_menu_id: showPageInfo(mTabControl.getCurrentTab(), false); break; case R.id.classic_history_menu_id: bookmarksOrHistoryPicker(true); break; case R.id.title_bar_share_page_url: case R.id.share_page_menu_id: Tab currentTab = mTabControl.getCurrentTab(); if (null == currentTab) { mCanChord = false; return false; } currentTab.populatePickerData(); sharePage(this, currentTab.getTitle(), currentTab.getUrl(), currentTab.getFavicon(), createScreenshot(currentTab.getWebView())); break;

case R.id.dump_nav_menu_id: getTopWindow().debugDump(); break; case R.id.dump_counters_menu_id: getTopWindow().dumpV8Counters(); break; case R.id.zoom_in_menu_id: getTopWindow().zoomIn(); break; case R.id.zoom_out_menu_id: getTopWindow().zoomOut(); break; case R.id.view_downloads_menu_id: viewDownloads(null); break; case case case case case case case case { int menuid = item.getItemId(); for (int id = 0; id < WINDOW_SHORTCUT_ID_ARRAY.length; id++) { if (WINDOW_SHORTCUT_ID_ARRAY[id] == menuid) { Tab desiredTab = mTabControl.getTab(id); if (desiredTab != null && desiredTab != mTabControl.getCurrentTab()) { switchToTab(id); } break; } } } break; default: if (!super.onOptionsItemSelected(item)) { return false; } // Otherwise fall through. } mCanChord = false; return true; } public void closeFind() { mMenuState = R.id.MAIN_MENU; } @Override public boolean onPrepareOptionsMenu(Menu menu) { // This happens when the user begins to hold down the menu key, so // allow them to chord to get a shortcut. mCanChord = true; // Note: setVisible will decide whether an item is visible; while // setEnabled() will decide whether an item is enabled, which also means // whether the matching shortcut key will function. super.onPrepareOptionsMenu(menu); switch (mMenuState) { case EMPTY_MENU: if (mCurrentMenuState != mMenuState) { menu.setGroupVisible(R.id.MAIN_MENU, false); menu.setGroupEnabled(R.id.MAIN_MENU, false); menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, false); } break; default: if (mCurrentMenuState != mMenuState) { menu.setGroupVisible(R.id.MAIN_MENU, true); R.id.window_one_menu_id: R.id.window_two_menu_id: R.id.window_three_menu_id: R.id.window_four_menu_id: R.id.window_five_menu_id: R.id.window_six_menu_id: R.id.window_seven_menu_id: R.id.window_eight_menu_id:

menu.setGroupEnabled(R.id.MAIN_MENU, true); menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, true); } final WebView w = getTopWindow(); boolean canGoBack = false; boolean canGoForward = false; boolean isHome = false; if (w != null) { canGoBack = w.canGoBack(); canGoForward = w.canGoForward(); isHome = mSettings.getHomePage().equals(w.getUrl()); } final MenuItem back = menu.findItem(R.id.back_menu_id); back.setEnabled(canGoBack); final MenuItem home = menu.findItem(R.id.homepage_menu_id); home.setEnabled(!isHome); menu.findItem(R.id.forward_menu_id) .setEnabled(canGoForward); menu.findItem(R.id.new_tab_menu_id).setEnabled( mTabControl.canCreateNewTab()); // decide whether to show the share link option PackageManager pm = getPackageManager(); Intent send = new Intent(Intent.ACTION_SEND); send.setType("text/plain"); ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY); menu.findItem(R.id.share_page_menu_id).setVisible(ri != null); boolean isNavDump = mSettings.isNavDump(); final MenuItem nav = menu.findItem(R.id.dump_nav_menu_id); nav.setVisible(isNavDump); nav.setEnabled(isNavDump); boolean showDebugSettings = mSettings.showDebugSettings(); final MenuItem counter = menu.findItem(R.id.dump_counters_menu_id); counter.setVisible(showDebugSettings); counter.setEnabled(showDebugSettings); break; } mCurrentMenuState = mMenuState; return true; } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { if (v instanceof TitleBar) { return; } WebView webview = (WebView) v; WebView.HitTestResult result = webview.getHitTestResult(); if (result == null) { return; } int type = result.getType(); if (type == WebView.HitTestResult.UNKNOWN_TYPE) { Log.w(LOGTAG, "We should not show context menu when nothing is touched"); return; } if (type == WebView.HitTestResult.EDIT_TEXT_TYPE) { // let TextView handles context menu return; } // Note, http://b/issue?id=1106666 is requesting that // an inflated menu can be used again. This is not available // yet, so inflate each time (yuk!) MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.browsercontext, menu); // Show the correct menu group String extra = result.getExtra();

menu.setGroupVisible(R.id.PHONE_MENU, type == WebView.HitTestResult.PHONE_TYPE); menu.setGroupVisible(R.id.EMAIL_MENU, type == WebView.HitTestResult.EMAIL_TYPE); menu.setGroupVisible(R.id.GEO_MENU, type == WebView.HitTestResult.GEO_TYPE); menu.setGroupVisible(R.id.IMAGE_MENU, type == WebView.HitTestResult.IMAGE_TYPE || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE); menu.setGroupVisible(R.id.ANCHOR_MENU, type == WebView.HitTestResult.SRC_ANCHOR_TYPE || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE); // Setup custom handling depending on the type switch (type) { case WebView.HitTestResult.PHONE_TYPE: menu.setHeaderTitle(Uri.decode(extra)); menu.findItem(R.id.dial_context_menu_id).setIntent( new Intent(Intent.ACTION_VIEW, Uri .parse(WebView.SCHEME_TEL + extra))); Intent addIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT); addIntent.putExtra(Insert.PHONE, Uri.decode(extra)); addIntent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); menu.findItem(R.id.add_contact_context_menu_id).setIntent( addIntent); menu.findItem(R.id.copy_phone_context_menu_id).setOnMenuItemClickListener( new Copy(extra)); break; case WebView.HitTestResult.EMAIL_TYPE: menu.setHeaderTitle(extra); menu.findItem(R.id.email_context_menu_id).setIntent( new Intent(Intent.ACTION_VIEW, Uri .parse(WebView.SCHEME_MAILTO + extra))); menu.findItem(R.id.copy_mail_context_menu_id).setOnMenuItemClickListener( new Copy(extra)); break; case WebView.HitTestResult.GEO_TYPE: menu.setHeaderTitle(extra); menu.findItem(R.id.map_context_menu_id).setIntent( new Intent(Intent.ACTION_VIEW, Uri .parse(WebView.SCHEME_GEO + URLEncoder.encode(extra)))); menu.findItem(R.id.copy_geo_context_menu_id).setOnMenuItemClickListener( new Copy(extra)); break; case WebView.HitTestResult.SRC_ANCHOR_TYPE: case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE: TextView titleView = (TextView) LayoutInflater.from(this) .inflate(android.R.layout.browser_link_context_header, null); titleView.setText(extra); menu.setHeaderView(titleView); // decide whether to show the open link in new tab option menu.findItem(R.id.open_newtab_context_menu_id).setVisible( mTabControl.canCreateNewTab()); menu.findItem(R.id.bookmark_context_menu_id).setVisible( Bookmarks.urlHasAcceptableScheme(extra)); PackageManager pm = getPackageManager(); Intent send = new Intent(Intent.ACTION_SEND); send.setType("text/plain"); ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY); menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null); if (type == WebView.HitTestResult.SRC_ANCHOR_TYPE) { break; } // otherwise fall through to handle image part case WebView.HitTestResult.IMAGE_TYPE: if (type == WebView.HitTestResult.IMAGE_TYPE) { menu.setHeaderTitle(extra); } menu.findItem(R.id.view_image_context_menu_id).setIntent( new Intent(Intent.ACTION_VIEW, Uri.parse(extra))); menu.findItem(R.id.download_context_menu_id). setOnMenuItemClickListener(new Download(extra)); menu.findItem(R.id.set_wallpaper_context_menu_id).

setOnMenuItemClickListener(new SetAsWallpaper(extra)); break; default: Log.w(LOGTAG, "We should not get here."); break; } hideFakeTitleBar(); } // Attach the given tab to the content view. // this should only be called for the current tab. private void attachTabToContentView(Tab t) { // Attach the container that contains the main WebView and any other UI // associated with the tab. t.attachTabToContentView(mContentView); if (mShouldShowErrorConsole) { ErrorConsoleView errorConsole = t.getErrorConsole(true); if (errorConsole.numberOfErrors() == 0) { errorConsole.showConsole(ErrorConsoleView.SHOW_NONE); } else { errorConsole.showConsole(ErrorConsoleView.SHOW_MINIMIZED); } mErrorConsoleContainer.addView(errorConsole, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); } WebView view = t.getWebView(); view.setEmbeddedTitleBar(mTitleBar); if (t.isInVoiceSearchMode()) { showVoiceTitleBar(t.getVoiceDisplayTitle()); } else { revertVoiceTitleBar(); } // Request focus on the top window. t.getTopWindow().requestFocus(); } // Attach a sub window to the main WebView of the given tab. void attachSubWindow(Tab t) { t.attachSubWindow(mContentView); getTopWindow().requestFocus(); } // Remove the given tab from the content view. private void removeTabFromContentView(Tab t) { // Remove the container that contains the main WebView. t.removeTabFromContentView(mContentView); ErrorConsoleView errorConsole = t.getErrorConsole(false); if (errorConsole != null) { mErrorConsoleContainer.removeView(errorConsole); } WebView view = t.getWebView(); if (view != null) { view.setEmbeddedTitleBar(null); } } // Remove the sub window if it exists. Also called by TabControl when the // user clicks the 'X' to dismiss a sub window. /* package */ void dismissSubWindow(Tab t) { t.removeSubWindow(mContentView); // dismiss the subwindow. This will destroy the WebView. t.dismissSubWindow(); getTopWindow().requestFocus(); } // A wrapper function of {@link #openTabAndShow(UrlData, boolean, String)} // that accepts url as string. private Tab openTabAndShow(String url, boolean closeOnExit, String appId) { return openTabAndShow(new UrlData(url), closeOnExit, appId); }

// // // /*

This method does a ton of stuff. It will attempt to create a new tab if we haven't reached MAX_TABS. Otherwise it uses the current tab. If url isn't null, it will load the given url. package */Tab openTabAndShow(UrlData urlData, boolean closeOnExit, String appId) { final Tab currentTab = mTabControl.getCurrentTab(); if (mTabControl.canCreateNewTab()) { final Tab tab = mTabControl.createNewTab(closeOnExit, appId, urlData.mUrl); WebView webview = tab.getWebView(); // If the last tab was removed from the active tabs page, currentTab // will be null. if (currentTab != null) { removeTabFromContentView(currentTab); } // We must set the new tab as the current tab to reflect the old // animation behavior. mTabControl.setCurrentTab(tab); attachTabToContentView(tab); if (!urlData.isEmpty()) { loadUrlDataIn(tab, urlData); } return tab; } else { // Get rid of the subwindow if it exists dismissSubWindow(currentTab); if (!urlData.isEmpty()) { // Load the given url. loadUrlDataIn(currentTab, urlData); } return currentTab; }

} private Tab openTab(String url) { if (mSettings.openInBackground()) { Tab t = mTabControl.createNewTab(); if (t != null) { WebView view = t.getWebView(); loadUrl(view, url); } return t; } else { return openTabAndShow(url, false, null); } } private class Copy implements OnMenuItemClickListener { private CharSequence mText; public boolean onMenuItemClick(MenuItem item) { copy(mText); return true; } public Copy(CharSequence toCopy) { mText = toCopy; } } private class Download implements OnMenuItemClickListener { private String mText; public boolean onMenuItemClick(MenuItem item) { onDownloadStartNoStream(mText, null, null, null, -1); return true; } public Download(String toDownload) { mText = toDownload; } } private class SetAsWallpaper extends Thread implements OnMenuItemClickListener, DialogInterface.OnCancelListener { private URL mUrl; private ProgressDialog mWallpaperProgress; private boolean mCanceled = false;

public SetAsWallpaper(String url) { try { mUrl = new URL(url); } catch (MalformedURLException e) { mUrl = null; } } public void onCancel(DialogInterface dialog) { mCanceled = true; } public boolean onMenuItemClick(MenuItem item) { if (mUrl != null) { // The user may have tried to set a image with a large file size as their // background so it may take a few moments to perform the operation. Display // a progress spinner while it is working. mWallpaperProgress = new ProgressDialog(BrowserActivity.this); mWallpaperProgress.setIndeterminate(true); mWallpaperProgress.setMessage(getText(R.string.progress_dialog_setting_wallpaper)); mWallpaperProgress.setCancelable(true); mWallpaperProgress.setOnCancelListener(this); mWallpaperProgress.show(); start(); } return true; } public void run() { Drawable oldWallpaper = BrowserActivity.this.getWallpaper(); try { // TODO: This will cause the resource to be downloaded again, when we // should in most cases be able to grab it from the cache. To fix this // we should query WebCore to see if we can access a cached version and // instead open an input stream on that. This pattern could also be used // in the download manager where the same problem exists. InputStream inputstream = mUrl.openStream(); if (inputstream != null) { setWallpaper(inputstream); } } catch (IOException e) { Log.e(LOGTAG, "Unable to set new wallpaper"); // Act as though the user canceled the operation so we try to // restore the old wallpaper. mCanceled = true; } if (mCanceled) { // Restore the old wallpaper if the user cancelled whilst we were setting // the new wallpaper. int width = oldWallpaper.getIntrinsicWidth(); int height = oldWallpaper.getIntrinsicHeight(); Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bm); oldWallpaper.setBounds(0, 0, width, height); oldWallpaper.draw(canvas); try { setWallpaper(bm); } catch (IOException e) { Log.e(LOGTAG, "Unable to restore old wallpaper."); } mCanceled = false; } if (mWallpaperProgress.isShowing()) { mWallpaperProgress.dismiss(); } } } private void copy(CharSequence text) { try { IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard")); if (clip != null) { clip.setClipboardText(text); } } catch (android.os.RemoteException e) {

Log.e(LOGTAG, "Copy failed", e); } } /** * Resets the browser title-view to whatever it must * (for example, if we had a loading error) * When we have a new page, we call resetTitle, when * have to reset the titlebar to whatever it used to * (for example, if the user chose to stop loading), * call resetTitleAndRevertLockIcon. */ /* package */ void resetTitleAndRevertLockIcon() { mTabControl.getCurrentTab().revertLockIcon(); updateLockIconToLatest(); resetTitleIconAndProgress(); }

be we be we

/** * Reset the title, favicon, and progress. */ private void resetTitleIconAndProgress() { WebView current = mTabControl.getCurrentWebView(); if (current == null) { return; } resetTitleAndIcon(current); int progress = current.getProgress(); current.getWebChromeClient().onProgressChanged(current, progress); } // Reset the title and the icon based on the given item. private void resetTitleAndIcon(WebView view) { WebHistoryItem item = view.copyBackForwardList().getCurrentItem(); if (item != null) { setUrlTitle(item.getUrl(), item.getTitle()); setFavicon(item.getFavicon()); } else { setUrlTitle(null, null); setFavicon(null); } } /** * Sets a title composed of the URL and the title string. * @param url The URL of the site being loaded. * @param title The title of the site being loaded. */ void setUrlTitle(String url, String title) { mUrl = url; mTitle = title; // If we are in voice search mode, the title has already been set. if (mTabControl.getCurrentTab().isInVoiceSearchMode()) return; mTitleBar.setDisplayTitle(url); mFakeTitleBar.setDisplayTitle(url); } /** * @param url The URL to build a title version of the URL from. * @return The title version of the URL or null if fails. * The title version of the URL can be either the URL hostname, * or the hostname with an "https://" prefix (for secure URLs), * or an empty string if, for example, the URL in question is a * file:// URL with no hostname. */ /* package */ static String buildTitleUrl(String url) { String titleUrl = null; if (url != null) { try { // parse the url string URL urlObj = new URL(url); if (urlObj != null) { titleUrl = ""; String protocol = urlObj.getProtocol(); String host = urlObj.getHost();

if (host != null && 0 < host.length()) { titleUrl = host; if (protocol != null) { // if a secure site, add an "https://" prefix! if (protocol.equalsIgnoreCase("https")) { titleUrl = protocol + "://" + host; } } } } } catch (MalformedURLException e) {} } return titleUrl; } // Set the favicon in the title bar. void setFavicon(Bitmap icon) { mTitleBar.setFavicon(icon); mFakeTitleBar.setFavicon(icon); } /** * Close the tab, remove its associated title bar, and adjust mTabControl's * current tab to a valid value. */ /* package */ void closeTab(Tab t) { int currentIndex = mTabControl.getCurrentIndex(); int removeIndex = mTabControl.getTabIndex(t); mTabControl.removeTab(t); if (currentIndex >= removeIndex && currentIndex != 0) { currentIndex--; } mTabControl.setCurrentTab(mTabControl.getTab(currentIndex)); resetTitleIconAndProgress(); } /* package */ void goBackOnePageOrQuit() { Tab current = mTabControl.getCurrentTab(); if (current == null) { /* * Instead of finishing the activity, simply push this to the back * of the stack and let ActivityManager to choose the foreground * activity. As BrowserActivity is singleTask, it will be always the * root of the task. So we can use either true or false for * moveTaskToBack(). */ moveTaskToBack(true); return; } WebView w = current.getWebView(); if (w.canGoBack()) { w.goBack(); } else { // Check to see if we are closing a window that was created by // another window. If so, we switch back to that window. Tab parent = current.getParentTab(); if (parent != null) { switchToTab(mTabControl.getTabIndex(parent)); // Now we close the other tab closeTab(current); } else { if (current.closeOnExit()) { // force the tab's inLoad() to be false as we are going to // either finish the activity or remove the tab. This will // ensure pauseWebViewTimers() taking action. mTabControl.getCurrentTab().clearInLoad(); if (mTabControl.getTabCount() == 1) { finish(); return; } // call pauseWebViewTimers() now, we won't be able to call // it in onPause() as the WebView won't be valid. // Temporarily change mActivityInPause to be true as // pauseWebViewTimers() will do nothing if mActivityInPause // is false. boolean savedState = mActivityInPause;

if (savedState) { Log.e(LOGTAG, "BrowserActivity is already paused " + "while handing goBackOnePageOrQuit."); } mActivityInPause = true; pauseWebViewTimers(); mActivityInPause = savedState; removeTabFromContentView(current); mTabControl.removeTab(current); } /* * Instead of finishing the activity, simply push * of the stack and let ActivityManager to choose * activity. As BrowserActivity is singleTask, it * root of the task. So we can use either true or * moveTaskToBack(). */ moveTaskToBack(true); } } } boolean isMenuDown() { return mMenuIsDown; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // Even if MENU is already held down, we need to call to super to open // the IME on long press. if (KeyEvent.KEYCODE_MENU == keyCode) { mMenuIsDown = true; return super.onKeyDown(keyCode, event); } // The default key mode is DEFAULT_KEYS_SEARCH_LOCAL. As the MENU is // still down, we don't want to trigger the search. Pretend to consume // the key and do nothing. if (mMenuIsDown) return true; switch(keyCode) { case KeyEvent.KEYCODE_SPACE: // WebView/WebTextView handle the keys in the KeyDown. As // the Activity's shortcut keys are only handled when WebView // doesn't, have to do it in onKeyDown instead of onKeyUp. if (event.isShiftPressed()) { getTopWindow().pageUp(false); } else { getTopWindow().pageDown(false); } return true; case KeyEvent.KEYCODE_BACK: if (event.getRepeatCount() == 0) { event.startTracking(); return true; } else if (mCustomView == null && mActiveTabsPage == null && event.isLongPress()) { bookmarksOrHistoryPicker(true); return true; } break; } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch(keyCode) { case KeyEvent.KEYCODE_MENU: mMenuIsDown = false; break; case KeyEvent.KEYCODE_BACK: if (event.isTracking() && !event.isCanceled()) { if (mCustomView != null) { // if a custom view is showing, hide it mTabControl.getCurrentWebView().getWebChromeClient() .onHideCustomView(); } else if (mActiveTabsPage != null) { // if tab page is showing, hide it

this to the back the foreground will be always the false for

removeActiveTabPage(true); } else { WebView subwindow = mTabControl.getCurrentSubWindow(); if (subwindow != null) { if (subwindow.canGoBack()) { subwindow.goBack(); } else { dismissSubWindow(mTabControl.getCurrentTab()); } } else { goBackOnePageOrQuit(); } } return true; } break; } return super.onKeyUp(keyCode, event); } /* package */ void stopLoading() { mDidStopLoad = true; resetTitleAndRevertLockIcon(); WebView w = getTopWindow(); w.stopLoading(); // FIXME: before refactor, it is using mWebViewClient. So I keep the // same logic here. But for subwindow case, should we call into the main // WebView's onPageFinished as we never call its onPageStarted and if // the page finishes itself, we don't call onPageFinished. mTabControl.getCurrentWebView().getWebViewClient().onPageFinished(w, w.getUrl()); cancelStopToast(); mStopToast = Toast .makeText(this, R.string.stopping, Toast.LENGTH_SHORT); mStopToast.show(); } boolean didUserStopLoading() { return mDidStopLoad; } private void cancelStopToast() { if (mStopToast != null) { mStopToast.cancel(); mStopToast = null; } } // called by a UI or non-UI thread to post the message public void postMessage(int what, int arg1, int arg2, Object obj, long delayMillis) { mHandler.sendMessageDelayed(mHandler.obtainMessage(what, arg1, arg2, obj), delayMillis); } // called by a UI or non-UI thread to remove the message void removeMessages(int what, Object object) { mHandler.removeMessages(what, object); } // public message ids public final static int LOAD_URL public final static int STOP_LOAD // Message Ids private static final int FOCUS_NODE_HREF private static final int RELEASE_WAKELOCK static final int UPDATE_BOOKMARK_THUMBNAIL

= 1001; = 1002;

= 102; = 107; = 108;

// Private handler for handling javascript and saving passwords private Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case FOCUS_NODE_HREF: {

String url = (String) msg.getData().get("url"); String title = (String) msg.getData().get("title"); if (url == null || url.length() == 0) { break; } HashMap focusNodeMap = (HashMap) msg.obj; WebView view = (WebView) focusNodeMap.get("webview"); // Only apply the action if the top window did not change. if (getTopWindow() != view) { break; } switch (msg.arg1) { case R.id.open_context_menu_id: case R.id.view_image_context_menu_id: loadUrlFromContext(getTopWindow(), url); break; case R.id.open_newtab_context_menu_id: final Tab parent = mTabControl.getCurrentTab(); final Tab newTab = openTab(url); if (newTab != parent) { parent.addChildTab(newTab); } break; case R.id.bookmark_context_menu_id: Intent intent = new Intent(BrowserActivity.this, AddBookmarkPage.class); intent.putExtra("url", url); intent.putExtra("title", title); startActivity(intent); break; case R.id.share_link_context_menu_id: // See if this site has been visited before StringBuilder sb = new StringBuilder( Browser.BookmarkColumns.URL + " = "); DatabaseUtils.appendEscapedSQLString(sb, url); Cursor c = mResolver.query(Browser.BOOKMARKS_URI, Browser.HISTORY_PROJECTION, sb.toString(), null, null); if (c.moveToFirst()) { // The site has been visited before, so grab the // info from the database. Bitmap favicon = null; Bitmap thumbnail = null; String linkTitle = c.getString(Browser. HISTORY_PROJECTION_TITLE_INDEX); byte[] data = c.getBlob(Browser. HISTORY_PROJECTION_FAVICON_INDEX); if (data != null) { favicon = BitmapFactory.decodeByteArray( data, 0, data.length); } data = c.getBlob(Browser. HISTORY_PROJECTION_THUMBNAIL_INDEX); if (data != null) { thumbnail = BitmapFactory.decodeByteArray( data, 0, data.length); } sharePage(BrowserActivity.this, linkTitle, url, favicon, thumbnail); } else { Browser.sendString(BrowserActivity.this, url, getString( R.string.choosertitle_sharevia)); } break; case R.id.copy_link_context_menu_id: copy(url); break; case R.id.save_link_context_menu_id: case R.id.download_context_menu_id: onDownloadStartNoStream(url, null, null, null, -1); break; } break; }

case LOAD_URL: loadUrlFromContext(getTopWindow(), (String) msg.obj); break; case STOP_LOAD: stopLoading(); break; case RELEASE_WAKELOCK: if (mWakeLock.isHeld()) { mWakeLock.release(); // if we reach here, Browser should be still in the // background loading after WAKELOCK_TIMEOUT (5-min). // To avoid burning the battery, stop loading. mTabControl.stopAllLoading(); } break; case UPDATE_BOOKMARK_THUMBNAIL: WebView view = (WebView) msg.obj; if (view != null) { updateScreenshot(view); } break; } } }; /** * Share a page, providing the title, url, favicon, and a screenshot. Uses * an {@link Intent} to launch the Activity chooser. * @param c Context used to launch a new Activity. * @param title Title of the page. Stored in the Intent with * {@link Intent#EXTRA_SUBJECT} * @param url URL of the page. Stored in the Intent with * {@link Intent#EXTRA_TEXT} * @param favicon Bitmap of the favicon for the page. Stored in the Intent * with {@link Browser#EXTRA_SHARE_FAVICON} * @param screenshot Bitmap of a screenshot of the page. Stored in the * Intent with {@link Browser#EXTRA_SHARE_SCREENSHOT} */ public static final void sharePage(Context c, String title, String url, Bitmap favicon, Bitmap screenshot) { Intent send = new Intent(Intent.ACTION_SEND); send.setType("text/plain"); send.putExtra(Intent.EXTRA_TEXT, url); send.putExtra(Intent.EXTRA_SUBJECT, title); send.putExtra(Browser.EXTRA_SHARE_FAVICON, favicon); send.putExtra(Browser.EXTRA_SHARE_SCREENSHOT, screenshot); try { c.startActivity(Intent.createChooser(send, c.getString( R.string.choosertitle_sharevia))); } catch(android.content.ActivityNotFoundException ex) { // if no app handles it, do nothing } } private void updateScreenshot(WebView view) { // If this is a bookmarked site, add a screenshot to the database. // FIXME: When should we update? Every time? // FIXME: Would like to make sure there is actually something to // draw, but the API for that (WebViewCore.pictureReady()) is not // currently accessible here. final Bitmap bm = createScreenshot(view); if (bm == null) { return; } final ContentResolver cr = getContentResolver(); final String url = view.getUrl(); final String originalUrl = view.getOriginalUrl(); new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... unused) { Cursor c = null; try {

c = BrowserBookmarksAdapter.queryBookmarksForUrl( cr, originalUrl, url, true); if (c != null) { if (c.moveToFirst()) { ContentValues values = new ContentValues(); final ByteArrayOutputStream os = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, 100, os); values.put(Browser.BookmarkColumns.THUMBNAIL, os.toByteArray()); do { cr.update(ContentUris.withAppendedId( Browser.BOOKMARKS_URI, c.getInt(0)), values, null, null); } while (c.moveToNext()); } } } catch (IllegalStateException e) { // Ignore } finally { if (c != null) c.close(); } return null; } }.execute(); } /** * Values for the size of the thumbnail created when taking a screenshot. * Lazily initialized. Instead of using these directly, use * getDesiredThumbnailWidth() or getDesiredThumbnailHeight(). */ private static int THUMBNAIL_WIDTH = 0; private static int THUMBNAIL_HEIGHT = 0; /** * Return the desired width for thumbnail screenshots, which are stored in * the database, and used on the bookmarks screen. * @param context Context for finding out the density of the screen. * @return int desired width for thumbnail screenshot. */ /* package */ static int getDesiredThumbnailWidth(Context context) { if (THUMBNAIL_WIDTH == 0) { float density = context.getResources().getDisplayMetrics().density; THUMBNAIL_WIDTH = (int) (90 * density); THUMBNAIL_HEIGHT = (int) (80 * density); } return THUMBNAIL_WIDTH; } /** * Return the desired height for thumbnail screenshots, which are stored in * the database, and used on the bookmarks screen. * @param context Context for finding out the density of the screen. * @return int desired height for thumbnail screenshot. */ /* package */ static int getDesiredThumbnailHeight(Context context) { // To ensure that they are both initialized. getDesiredThumbnailWidth(context); return THUMBNAIL_HEIGHT; } private Bitmap createScreenshot(WebView view) { Picture thumbnail = view.capturePicture(); if (thumbnail == null) { return null; } Bitmap bm = Bitmap.createBitmap(getDesiredThumbnailWidth(this), getDesiredThumbnailHeight(this), Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bm); // May need to tweak these values to determine what is the // best scale factor int thumbnailWidth = thumbnail.getWidth(); int thumbnailHeight = thumbnail.getHeight(); float scaleFactorX = 1.0f; float scaleFactorY = 1.0f; if (thumbnailWidth > 0) { scaleFactorX = (float) getDesiredThumbnailWidth(this) /

(float)thumbnailWidth; } else { return null; } if (view.getWidth() > view.getHeight() && thumbnailHeight < view.getHeight() && thumbnailHeight > 0) { // If the device is in landscape and the page is shorter // than the height of the view, stretch the thumbnail to fill the // space. scaleFactorY = (float) getDesiredThumbnailHeight(this) / (float)thumbnailHeight; } else { // In the portrait case, this looks nice. scaleFactorY = scaleFactorX; } canvas.scale(scaleFactorX, scaleFactorY); thumbnail.draw(canvas); return bm; } // ------------------------------------------------------------------------// Helper function for WebViewClient. //------------------------------------------------------------------------// /* /* /* /* Use in overrideUrlLoading package */ final static String package */ final static String package */ final static String package */ final static String

SCHEME_WTAI = "wtai://wp/"; SCHEME_WTAI_MC = "wtai://wp/mc;"; SCHEME_WTAI_SD = "wtai://wp/sd;"; SCHEME_WTAI_AP = "wtai://wp/ap;";

// Keep this initial progress in sync with initialProgressValue (* 100) // in ProgressTracker.cpp private final static int INITIAL_PROGRESS = 10; void onPageStarted(WebView view, String url, Bitmap favicon) { // when BrowserActivity just starts, onPageStarted may be called before // onResume as it is triggered from onCreate. Call resumeWebViewTimers // to start the timer. As we won't switch tabs while an activity is in // pause state, we can ensure calling resume and pause in pair. if (mActivityInPause) resumeWebViewTimers(); resetLockIcon(url); setUrlTitle(url, null); setFavicon(favicon); // Show some progress so that the user knows the page is beginning to // load onProgressChanged(view, INITIAL_PROGRESS); mDidStopLoad = false; if (!mIsNetworkUp) createAndShowNetworkDialog(); if (mSettings.isTracing()) { String host; try { WebAddress uri = new WebAddress(url); host = uri.mHost; } catch (android.net.ParseException ex) { host = "browser"; } host = host.replace('.', '_'); host += ".trace"; mInTrace = true; Debug.startMethodTracing(host, 20 * 1024 * 1024); } // Performance probe if (false) { mStart = SystemClock.uptimeMillis(); mProcessStart = Process.getElapsedCpuTime(); long[] sysCpu = new long[7]; if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null, sysCpu, null)) { mUserStart = sysCpu[0] + sysCpu[1]; mSystemStart = sysCpu[2]; mIdleStart = sysCpu[3]; mIrqStart = sysCpu[4] + sysCpu[5] + sysCpu[6];

} mUiStart = SystemClock.currentThreadTimeMillis(); } } void onPageFinished(WebView view, String url) { // Reset the title and icon in case we stopped a provisional load. resetTitleAndIcon(view); // Update the lock icon image only once we are done loading updateLockIconToLatest(); // pause the WebView timer and release the wake lock if it is finished // while BrowserActivity is in pause state. if (mActivityInPause && pauseWebViewTimers()) { if (mWakeLock.isHeld()) { mHandler.removeMessages(RELEASE_WAKELOCK); mWakeLock.release(); } } // Performance probe if (false) { long[] sysCpu = new long[7]; if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null, sysCpu, null)) { String uiInfo = "UI thread used " + (SystemClock.currentThreadTimeMillis() - mUiStart) + " ms"; if (LOGD_ENABLED) { Log.d(LOGTAG, uiInfo); } //The string that gets written to the log String performanceString = "It took total " + (SystemClock.uptimeMillis() - mStart) + " ms clock time to load the page." + "\nbrowser process used " + (Process.getElapsedCpuTime() - mProcessStart) + " ms, user processes used " + (sysCpu[0] + sysCpu[1] - mUserStart) * 10 + " ms, kernel used " + (sysCpu[2] - mSystemStart) * 10 + " ms, idle took " + (sysCpu[3] - mIdleStart) * 10 + " ms and irq took " + (sysCpu[4] + sysCpu[5] + sysCpu[6] - mIrqStart) * 10 + " ms, " + uiInfo; if (LOGD_ENABLED) { Log.d(LOGTAG, performanceString + "\nWebpage: " + url); } if (url != null) { // strip the url to maintain consistency String newUrl = new String(url); if (newUrl.startsWith("http://www.")) { newUrl = newUrl.substring(11); } else if (newUrl.startsWith("http://")) { newUrl = newUrl.substring(7); } else if (newUrl.startsWith("https://www.")) { newUrl = newUrl.substring(12); } else if (newUrl.startsWith("https://")) { newUrl = newUrl.substring(8); } if (LOGD_ENABLED) { Log.d(LOGTAG, newUrl + " loaded"); } } } } if (mInTrace) { mInTrace = false; Debug.stopMethodTracing(); } } boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith(SCHEME_WTAI)) { // wtai://wp/mc;number // number=string(phone-number) if (url.startsWith(SCHEME_WTAI_MC)) { Intent intent = new Intent(Intent.ACTION_VIEW,

Uri.parse(WebView.SCHEME_TEL + url.substring(SCHEME_WTAI_MC.length()))); startActivity(intent); return true; } // wtai://wp/sd;dtmf // dtmf=string(dialstring) if (url.startsWith(SCHEME_WTAI_SD)) { // TODO: only send when there is active voice connection return false; } // wtai://wp/ap;number;name // number=string(phone-number) // name=string if (url.startsWith(SCHEME_WTAI_AP)) { // TODO return false; } } // The "about:" schemes are internal to the browser; don't want these to // be dispatched to other apps. if (url.startsWith("about:")) { return false; } Intent intent; // perform generic parsing of the URI to turn it into an Intent. try { intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); } catch (URISyntaxException ex) { Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage()); return false; } // check whether the intent can be resolved. If not, we will see // whether we can download it from the Market. if (getPackageManager().resolveActivity(intent, 0) == null) { String packagename = intent.getPackage(); if (packagename != null) { intent = new Intent(Intent.ACTION_VIEW, Uri .parse("market://search?q=pname:" + packagename)); intent.addCategory(Intent.CATEGORY_BROWSABLE); startActivity(intent); return true; } else { return false; } } // sanitize the Intent, ensuring web pages can not bypass browser // security (only access to BROWSABLE activities). intent.addCategory(Intent.CATEGORY_BROWSABLE); intent.setComponent(null); try { if (startActivityIfNeeded(intent, -1)) { return true; } } catch (ActivityNotFoundException ex) { // ignore the error. If no application can handle the URL, // eg about:blank, assume the browser can handle it. } if (mMenuIsDown) { openTab(url); closeOptionsMenu(); return true; } return false; } // ------------------------------------------------------------------------// Helper function for WebChromeClient // ------------------------------------------------------------------------void onProgressChanged(WebView view, int newProgress) { mFakeTitleBar.setProgress(newProgress);

if (newProgress == 100) { // onProgressChanged() may continue to be called after the main // frame has finished loading, as any remaining sub frames continue // to load. We'll only get called once though with newProgress as // 100 when everything is loaded. (onPageFinished is called once // when the main frame completes loading regardless of the state of // any sub frames so calls to onProgressChanges may continue after // onPageFinished has executed) if (mInLoad) { mInLoad = false; updateInLoadMenuItems(); // If the options menu is open, leave the title bar if (!mOptionsMenuOpen || !mIconView) { hideFakeTitleBar(); } } } else { if (!mInLoad) { // onPageFinished may have already been called but a subframe is // still loading and updating the progress. Reset mInLoad and // update the menu items. mInLoad = true; updateInLoadMenuItems(); } // When the page first begins to load, the Activity may still be // paused, in which case showFakeTitleBar will do nothing. Call // again as the page continues to load so that it will be shown. // (Calling it will the fake title bar is already showing will also // do nothing. if (!mOptionsMenuOpen || mIconView) { // This page has begun to load, so show the title bar showFakeTitleBar(); } } } void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) { // if a view already exists then immediately terminate the new one if (mCustomView != null) { callback.onCustomViewHidden(); return; } // Add the custom view to its container. mCustomViewContainer.addView(view, COVER_SCREEN_GRAVITY_CENTER); mCustomView = view; mCustomViewCallback = callback; // Save the menu state and set it to empty while the custom // view is showing. mOldMenuState = mMenuState; mMenuState = EMPTY_MENU; // Hide the content view. mContentView.setVisibility(View.GONE); // Finally show the custom view container. setStatusBarVisibility(false); mCustomViewContainer.setVisibility(View.VISIBLE); mCustomViewContainer.bringToFront(); } void onHideCustomView() { if (mCustomView == null) return; // Hide the custom view. mCustomView.setVisibility(View.GONE); // Remove the custom view from its container. mCustomViewContainer.removeView(mCustomView); mCustomView = null; // Reset the old menu state. mMenuState = mOldMenuState; mOldMenuState = EMPTY_MENU; mCustomViewContainer.setVisibility(View.GONE); mCustomViewCallback.onCustomViewHidden(); // Show the content view. setStatusBarVisibility(true); mContentView.setVisibility(View.VISIBLE); }

Bitmap getDefaultVideoPoster() { if (mDefaultVideoPoster == null) { mDefaultVideoPoster = BitmapFactory.decodeResource( getResources(), R.drawable.default_video_poster); } return mDefaultVideoPoster; } View getVideoLoadingProgressView() { if (mVideoProgressView == null) { LayoutInflater inflater = LayoutInflater.from(BrowserActivity.this); mVideoProgressView = inflater.inflate( R.layout.video_loading_progress, null); } return mVideoProgressView; } /* * The Object used to inform the WebView of the file to upload. */ private ValueCallback<Uri> mUploadMessage; void openFileChooser(ValueCallback<Uri> uploadMsg) { if (mUploadMessage != null) return; mUploadMessage = uploadMsg; Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("*/*"); BrowserActivity.this.startActivityForResult(Intent.createChooser(i, getString(R.string.choose_upload)), FILE_SELECTED); } // ------------------------------------------------------------------------// Implement functions for DownloadListener // ------------------------------------------------------------------------/** * Notify the host application a download should be done, or that * the data should be streamed if a streaming viewer is available. * @param url The full url to the content that should be downloaded * @param contentDisposition Content-disposition http header, if * present. * @param mimetype The mimetype of the content reported by the server * @param contentLength The file size reported by the server */ public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { // if we're dealing wih A/V content that's not explicitly marked // for download, check if it's streamable. if (contentDisposition == null || !contentDisposition.regionMatches( true, 0, "attachment", 0, 10)) { // query the package manager to see if there's a registered handler // that matches. Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse(url), mimetype); ResolveInfo info = getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); if (info != null) { ComponentName myName = getComponentName(); // If we resolved to ourselves, we don't want to attempt to // load the url only to try and download it again. if (!myName.getPackageName().equals( info.activityInfo.packageName) || !myName.getClassName().equals( info.activityInfo.name)) { // someone (other than us) knows how to handle this mime // type with this scheme, don't download. try { startActivity(intent); return; } catch (ActivityNotFoundException ex) { if (LOGD_ENABLED) { Log.d(LOGTAG, "activity not found for " + mimetype + " over " + Uri.parse(url).getScheme(), ex); } // Best behavior is to fall back to a download in this

// case } } } } onDownloadStartNoStream(url, userAgent, contentDisposition, mimetype, contentLength); } // This is to work around the fact that java.net.URI throws Exceptions // instead of just encoding URL's properly // Helper method for onDownloadStartNoStream private static String encodePath(String path) { char[] chars = path.toCharArray(); boolean needed = false; for (char c : chars) { if (c == '[' || c == ']') { needed = true; break; } } if (needed == false) { return path; } StringBuilder sb = new StringBuilder(""); for (char c : chars) { if (c == '[' || c == ']') { sb.append('%'); sb.append(Integer.toHexString(c)); } else { sb.append(c); } } return sb.toString(); } /** * Notify the host application a download should be done, even if there * is a streaming viewer available for thise type. * @param url The full url to the content that should be downloaded * @param contentDisposition Content-disposition http header, if * present. * @param mimetype The mimetype of the content reported by the server * @param contentLength The file size reported by the server */ /*package */ void onDownloadStartNoStream(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); // Check to see if we have an SDCard String status = Environment.getExternalStorageState(); if (!status.equals(Environment.MEDIA_MOUNTED)) { int title; String msg; // Check to see if the SDCard is busy, same as the music app if (status.equals(Environment.MEDIA_SHARED)) { msg = getString(R.string.download_sdcard_busy_dlg_msg); title = R.string.download_sdcard_busy_dlg_title; } else { msg = getString(R.string.download_no_sdcard_dlg_msg, filename); title = R.string.download_no_sdcard_dlg_title; } new AlertDialog.Builder(this) .setTitle(title) .setIcon(android.R.drawable.ic_dialog_alert) .setMessage(msg) .setPositiveButton(R.string.ok, null) .show(); return; } // java.net.URI is a lot stricter than KURL so we have to encode some

// extra characters. Fix for b 2538060 and b 1634719 WebAddress webAddress; try { webAddress = new WebAddress(url); webAddress.mPath = encodePath(webAddress.mPath); } catch (Exception e) { // This only happens for very bad urls, we want to chatch the // exception here Log.e(LOGTAG, "Exception trying to parse url:" + url); return; } // XXX: Have to use the old url since the cookies were stored using the // old percent-encoded url. String cookies = CookieManager.getInstance().getCookie(url); ContentValues values = new ContentValues(); values.put(Downloads.Impl.COLUMN_URI, webAddress.toString()); values.put(Downloads.Impl.COLUMN_COOKIE_DATA, cookies); values.put(Downloads.Impl.COLUMN_USER_AGENT, userAgent); values.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, getPackageName()); values.put(Downloads.Impl.COLUMN_NOTIFICATION_CLASS, OpenDownloadReceiver.class.getCanonicalName()); values.put(Downloads.Impl.COLUMN_VISIBILITY, Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); values.put(Downloads.Impl.COLUMN_MIME_TYPE, mimetype); values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT, filename); values.put(Downloads.Impl.COLUMN_DESCRIPTION, webAddress.mHost); if (contentLength > 0) { values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, contentLength); } if (mimetype == null) { // We must have long pressed on a link or image to download it. We // are not sure of the mimetype in this case, so do a head request new FetchUrlMimeType(this).execute(values); } else { final Uri contentUri = getContentResolver().insert(Downloads.Impl.CONTENT_URI, values); } Toast.makeText(this, R.string.download_pending, Toast.LENGTH_SHORT) .show(); } // ------------------------------------------------------------------------/** * Resets the lock icon. This method is called when we start a new load and * know the url to be loaded. */ private void resetLockIcon(String url) { // Save the lock-icon state (we revert to it if the load gets cancelled) mTabControl.getCurrentTab().resetLockIcon(url); updateLockIconImage(LOCK_ICON_UNSECURE); } /** * Update the lock icon to correspond to our latest state. */ private void updateLockIconToLatest() { updateLockIconImage(mTabControl.getCurrentTab().getLockIconType()); } /** * Updates the lock-icon image in the title-bar. */ private void updateLockIconImage(int lockIconType) { Drawable d = null; if (lockIconType == LOCK_ICON_SECURE) { d = mSecLockIcon; } else if (lockIconType == LOCK_ICON_MIXED) { d = mMixLockIcon; } mTitleBar.setLock(d); mFakeTitleBar.setLock(d); } /**

* Displays a page-info dialog. * @param tab The tab to show info about * @param fromShowSSLCertificateOnError The flag that indicates whether * this dialog was opened from the SSL-certificate-on-error dialog or * not. This is important, since we need to know whether to return to * the parent dialog or simply dismiss. */ private void showPageInfo(final Tab tab, final boolean fromShowSSLCertificateOnError) { final LayoutInflater factory = LayoutInflater .from(this); final View pageInfoView = factory.inflate(R.layout.page_info, null); final WebView view = tab.getWebView(); String url = null; String title = null; if (view == null) { url = tab.getUrl(); title = tab.getTitle(); } else if (view == mTabControl.getCurrentWebView()) { // Use the cached title and url if this is the current WebView url = mUrl; title = mTitle; } else { url = view.getUrl(); title = view.getTitle(); } if (url == null) { url = ""; } if (title == null) { title = ""; } ((TextView) pageInfoView.findViewById(R.id.address)).setText(url); ((TextView) pageInfoView.findViewById(R.id.title)).setText(title); mPageInfoView = tab; mPageInfoFromShowSSLCertificateOnError = fromShowSSLCertificateOnError; AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this) .setTitle(R.string.page_info).setIcon(android.R.drawable.ic_dialog_info) .setView(pageInfoView) .setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mPageInfoDialog = null; mPageInfoView = null; // if we came here from the SSL error dialog if (fromShowSSLCertificateOnError) { // go back to the SSL error dialog showSSLCertificateOnError( mSSLCertificateOnErrorView, mSSLCertificateOnErrorHandler, mSSLCertificateOnErrorError); } } }) .setOnCancelListener( new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { mPageInfoDialog = null; mPageInfoView = null; // if we came here from the SSL error dialog if (fromShowSSLCertificateOnError) { // go back to the SSL error dialog showSSLCertificateOnError( mSSLCertificateOnErrorView, mSSLCertificateOnErrorHandler,

mSSLCertificateOnErrorError); } } }); // if we have a main top-level page SSL certificate set or a certificate // error if (fromShowSSLCertificateOnError || (view != null && view.getCertificate() != null)) { // add a 'View Certificate' button alertDialogBuilder.setNeutralButton( R.string.view_certificate, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mPageInfoDialog = null; mPageInfoView = null; // if we came here from the SSL error dialog if (fromShowSSLCertificateOnError) { // go back to the SSL error dialog showSSLCertificateOnError( mSSLCertificateOnErrorView, mSSLCertificateOnErrorHandler, mSSLCertificateOnErrorError); } else { // otherwise, display the top-most certificate from // the chain if (view.getCertificate() != null) { showSSLCertificate(tab); } } } }); } mPageInfoDialog = alertDialogBuilder.show(); } /** * Displays the main top-level page SSL certificate dialog * (accessible from the Page-Info dialog). * @param tab The tab to show certificate for. */ private void showSSLCertificate(final Tab tab) { final View certificateView = inflateCertificateView(tab.getWebView().getCertificate()); if (certificateView == null) { return; } LayoutInflater factory = LayoutInflater.from(this); final LinearLayout placeholder = (LinearLayout)certificateView.findViewById(R.id.placeholder); LinearLayout ll = (LinearLayout) factory.inflate( R.layout.ssl_success, placeholder); ((TextView)ll.findViewById(R.id.success)) .setText(R.string.ssl_certificate_is_valid); mSSLCertificateView = tab; mSSLCertificateDialog = new AlertDialog.Builder(this) .setTitle(R.string.ssl_certificate).setIcon( R.drawable.ic_dialog_browser_certificate_secure) .setView(certificateView) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mSSLCertificateDialog = null; mSSLCertificateView = null; showPageInfo(tab, false); } }) .setOnCancelListener(

new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { mSSLCertificateDialog = null; mSSLCertificateView = null; showPageInfo(tab, false); } }) .show(); } /** * Displays the SSL error certificate dialog. * @param view The target web-view. * @param handler The SSL error handler responsible for cancelling the * connection that resulted in an SSL error or proceeding per user request. * @param error The SSL error object. */ void showSSLCertificateOnError( final WebView view, final SslErrorHandler handler, final SslError error) { final View certificateView = inflateCertificateView(error.getCertificate()); if (certificateView == null) { return; } LayoutInflater factory = LayoutInflater.from(this); final LinearLayout placeholder = (LinearLayout)certificateView.findViewById(R.id.placeholder); if (error.hasError(SslError.SSL_UNTRUSTED)) { LinearLayout ll = (LinearLayout)factory .inflate(R.layout.ssl_warning, placeholder); ((TextView)ll.findViewById(R.id.warning)) .setText(R.string.ssl_untrusted); } if (error.hasError(SslError.SSL_IDMISMATCH)) { LinearLayout ll = (LinearLayout)factory .inflate(R.layout.ssl_warning, placeholder); ((TextView)ll.findViewById(R.id.warning)) .setText(R.string.ssl_mismatch); } if (error.hasError(SslError.SSL_EXPIRED)) { LinearLayout ll = (LinearLayout)factory .inflate(R.layout.ssl_warning, placeholder); ((TextView)ll.findViewById(R.id.warning)) .setText(R.string.ssl_expired); } if (error.hasError(SslError.SSL_NOTYETVALID)) { LinearLayout ll = (LinearLayout)factory .inflate(R.layout.ssl_warning, placeholder); ((TextView)ll.findViewById(R.id.warning)) .setText(R.string.ssl_not_yet_valid); } mSSLCertificateOnErrorHandler = handler; mSSLCertificateOnErrorView = view; mSSLCertificateOnErrorError = error; mSSLCertificateOnErrorDialog = new AlertDialog.Builder(this) .setTitle(R.string.ssl_certificate).setIcon( R.drawable.ic_dialog_browser_certificate_partially_secure) .setView(certificateView) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mSSLCertificateOnErrorDialog = null; mSSLCertificateOnErrorView = null; mSSLCertificateOnErrorHandler = null; mSSLCertificateOnErrorError = null; view.getWebViewClient().onReceivedSslError(

view, handler, error); } }) .setNeutralButton(R.string.page_info_view, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mSSLCertificateOnErrorDialog = null; // do not clear the dialog state: we will // need to show the dialog again once the // user is done exploring the page-info details showPageInfo(mTabControl.getTabFromView(view), true); } }) .setOnCancelListener( new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { mSSLCertificateOnErrorDialog = null; mSSLCertificateOnErrorView = null; mSSLCertificateOnErrorHandler = null; mSSLCertificateOnErrorError = null; view.getWebViewClient().onReceivedSslError( view, handler, error); } }) .show(); } /** * Inflates the SSL certificate view (helper method). * @param certificate The SSL certificate. * @return The resultant certificate view with issued-to, issued-by, * issued-on, expires-on, and possibly other fields set. * If the input certificate is null, returns null. */ private View inflateCertificateView(SslCertificate certificate) { if (certificate == null) { return null; } LayoutInflater factory = LayoutInflater.from(this); View certificateView = factory.inflate( R.layout.ssl_certificate, null); // issued to: SslCertificate.DName issuedTo = certificate.getIssuedTo(); if (issuedTo != null) { ((TextView) certificateView.findViewById(R.id.to_common)) .setText(issuedTo.getCName()); ((TextView) certificateView.findViewById(R.id.to_org)) .setText(issuedTo.getOName()); ((TextView) certificateView.findViewById(R.id.to_org_unit)) .setText(issuedTo.getUName()); } // issued by: SslCertificate.DName issuedBy = certificate.getIssuedBy(); if (issuedBy != null) { ((TextView) certificateView.findViewById(R.id.by_common)) .setText(issuedBy.getCName()); ((TextView) certificateView.findViewById(R.id.by_org)) .setText(issuedBy.getOName()); ((TextView) certificateView.findViewById(R.id.by_org_unit)) .setText(issuedBy.getUName()); } // issued on: String issuedOn = formatCertificateDate( certificate.getValidNotBeforeDate()); ((TextView) certificateView.findViewById(R.id.issued_on)) .setText(issuedOn); // expires on:

String expiresOn = formatCertificateDate( certificate.getValidNotAfterDate()); ((TextView) certificateView.findViewById(R.id.expires_on)) .setText(expiresOn); return certificateView; } /** * Formats the certificate date to a properly localized date string. * @return Properly localized version of the certificate date string and * the "" if it fails to localize. */ private String formatCertificateDate(Date certificateDate) { if (certificateDate == null) { return ""; } String formattedDate = DateFormat.getDateFormat(this).format(certificateDate); if (formattedDate == null) { return ""; } return formattedDate; } /** * Displays an http-authentication dialog. */ void showHttpAuthentication(final HttpAuthHandler handler, final String host, final String realm, final String title, final String name, final String password, int focusId) { LayoutInflater factory = LayoutInflater.from(this); final View v = factory .inflate(R.layout.http_authentication, null); if (name != null) { ((EditText) v.findViewById(R.id.username_edit)).setText(name); } if (password != null) { ((EditText) v.findViewById(R.id.password_edit)).setText(password); } String titleText = title; if (titleText == null) { titleText = getText(R.string.sign_in_to).toString().replace( "%s1", host).replace("%s2", realm); } mHttpAuthHandler = handler; AlertDialog dialog = new AlertDialog.Builder(this) .setTitle(titleText) .setIcon(android.R.drawable.ic_dialog_alert) .setView(v) .setPositiveButton(R.string.action, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String nm = ((EditText) v .findViewById(R.id.username_edit)) .getText().toString(); String pw = ((EditText) v .findViewById(R.id.password_edit)) .getText().toString(); BrowserActivity.this.setHttpAuthUsernamePassword (host, realm, nm, pw); handler.proceed(nm, pw); mHttpAuthenticationDialog = null; mHttpAuthHandler = null; }}) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { handler.cancel(); BrowserActivity.this.resetTitleAndRevertLockIcon(); mHttpAuthenticationDialog = null; mHttpAuthHandler = null; }}) .setOnCancelListener(new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) {

handler.cancel(); BrowserActivity.this.resetTitleAndRevertLockIcon(); mHttpAuthenticationDialog = null; mHttpAuthHandler = null; }}) .create(); // Make the IME appear when the dialog is displayed if applicable. dialog.getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); dialog.show(); if (focusId != 0) { dialog.findViewById(focusId).requestFocus(); } else { v.findViewById(R.id.username_edit).requestFocus(); } mHttpAuthenticationDialog = dialog; } public int getProgress() { WebView w = mTabControl.getCurrentWebView(); if (w != null) { return w.getProgress(); } else { return 100; } } /** * Set HTTP authentication password. * * @param host The host for the password * @param realm The realm for the password * @param username The username for the password. If it is null, it means * password can't be saved. * @param password The password */ public void setHttpAuthUsernamePassword(String host, String realm, String username, String password) { WebView w = getTopWindow(); if (w != null) { w.setHttpAuthUsernamePassword(host, realm, username, password); } } /** * connectivity manager says net has come or gone... inform the user * @param up true if net has come up, false if net has gone down */ public void onNetworkToggle(boolean up) { if (up == mIsNetworkUp) { return; } else if (up) { mIsNetworkUp = true; if (mAlertDialog != null) { mAlertDialog.cancel(); mAlertDialog = null; } } else { mIsNetworkUp = false; if (mInLoad) { createAndShowNetworkDialog(); } } WebView w = mTabControl.getCurrentWebView(); if (w != null) { w.setNetworkAvailable(up); } } boolean isNetworkUp() { return mIsNetworkUp; } // This method shows the network dialog alerting the user that the net is // down. It will only show the dialog if mAlertDialog is null. private void createAndShowNetworkDialog() { if (mAlertDialog == null) {

mAlertDialog = new AlertDialog.Builder(this) .setTitle(R.string.loadSuspendedTitle) .setMessage(R.string.loadSuspended) .setPositiveButton(R.string.ok, null) .show(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if (getTopWindow() == null) return; switch (requestCode) { case COMBO_PAGE: if (resultCode == RESULT_OK && intent != null) { String data = intent.getAction(); Bundle extras = intent.getExtras(); if (extras != null && extras.getBoolean("new_window", false)) { openTab(data); } else { final Tab currentTab = mTabControl.getCurrentTab(); dismissSubWindow(currentTab); if (data != null && data.length() != 0) { loadUrl(getTopWindow(), data); } } } // Deliberately fall through to PREFERENCES_PAGE, since the // same extra may be attached to the COMBO_PAGE case PREFERENCES_PAGE: if (resultCode == RESULT_OK && intent != null) { String action = intent.getStringExtra(Intent.EXTRA_TEXT); if (BrowserSettings.PREF_CLEAR_HISTORY.equals(action)) { mTabControl.removeParentChildRelationShips(); } } break; // Choose a file from the file picker. case FILE_SELECTED: if (null == mUploadMessage) break; Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData(); mUploadMessage.onReceiveValue(result); mUploadMessage = null; break; default: break; } getTopWindow().requestFocus(); } /* * This method is called as a result of the user selecting the options * menu to see the download window. It shows the download window on top of * the current window. */ private void viewDownloads(Uri downloadRecord) { Intent intent = new Intent(this, BrowserDownloadPage.class); intent.setData(downloadRecord); startActivityForResult(intent, BrowserActivity.DOWNLOAD_PAGE); } /** * Open the Go page. * @param startWithHistory If true, open starting on the history tab. * Otherwise, start with the bookmarks tab. */ /* package */ void bookmarksOrHistoryPicker(boolean startWithHistory) { WebView current = mTabControl.getCurrentWebView(); if (current == null) { return; } Intent intent = new Intent(this, CombinedBookmarkHistoryActivity.class);

String title = current.getTitle(); String url = current.getUrl(); Bitmap thumbnail = createScreenshot(current); // Just in case the user opens bookmarks before a page finishes loading // so the current history item, and therefore the page, is null. if (null == url) { url = mLastEnteredUrl; // This can happen. if (null == url) { url = mSettings.getHomePage(); } } // In case the web page has not yet received its associated title. if (title == null) { title = url; } intent.putExtra("title", title); intent.putExtra("url", url); intent.putExtra("thumbnail", thumbnail); // Disable opening in a new window if we have maxed out the windows intent.putExtra("disable_new_window", !mTabControl.canCreateNewTab()); intent.putExtra("touch_icon_url", current.getTouchIconUrl()); if (startWithHistory) { intent.putExtra(CombinedBookmarkHistoryActivity.STARTING_TAB, CombinedBookmarkHistoryActivity.HISTORY_TAB); } startActivityForResult(intent, COMBO_PAGE); } // Called when loading from context menu or LOAD_URL message private void loadUrlFromContext(WebView view, String url) { // In case the user enters nothing. if (url != null && url.length() != 0 && view != null) { url = smartUrlFilter(url); if (!view.getWebViewClient().shouldOverrideUrlLoading(view, url)) { loadUrl(view, url); } } } /** * Load the URL into the given WebView and update the title bar * to reflect the new load. Call this instead of WebView.loadUrl * directly. * @param view The WebView used to load url. * @param url The URL to load. */ private void loadUrl(WebView view, String url) { updateTitleBarForNewLoad(view, url); view.loadUrl(url); } /** * Load UrlData into a Tab and update the title bar to reflect the new * load. Call this instead of UrlData.loadIn directly. * @param t The Tab used to load. * @param data The UrlData being loaded. */ private void loadUrlDataIn(Tab t, UrlData data) { updateTitleBarForNewLoad(t.getWebView(), data.mUrl); data.loadIn(t); } /** * If the WebView is the top window, update the title bar to reflect * loading the new URL. i.e. set its text, clear the favicon (which * will be set once the page begins loading), and set the progress to * INITIAL_PROGRESS to show that the page has begun to load. Called * by loadUrl and loadUrlDataIn. * @param view The WebView that is starting a load. * @param url The URL that is being loaded. */ private void updateTitleBarForNewLoad(WebView view, String url) { if (view == getTopWindow()) { setUrlTitle(url, null); setFavicon(null); onProgressChanged(view, INITIAL_PROGRESS);

} } private String smartUrlFilter(Uri inUri) { if (inUri != null) { return smartUrlFilter(inUri.toString()); } return null; } protected static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile( "(?i)" + // switch on case insensitive matching "(" + // begin group for schema "(?:http|https|file):\\/\\/" + "|(?:inline|data|about|content|javascript):" + ")" + "(.*)" ); /** * Attempts to determine whether user input is a URL or search * terms. Anything with a space is passed to search. * * Converts to lowercase any mistakenly uppercased schema (i.e., * "Http://" converts to "http://" * * @return Original or modified URL * */ String smartUrlFilter(String url) { String inUrl = url.trim(); boolean hasSpace = inUrl.indexOf(' ') != -1; Matcher matcher = ACCEPTED_URI_SCHEMA.matcher(inUrl); if (matcher.matches()) { // force scheme to lowercase String scheme = matcher.group(1); String lcScheme = scheme.toLowerCase(); if (!lcScheme.equals(scheme)) { inUrl = lcScheme + matcher.group(2); } if (hasSpace) { inUrl = inUrl.replace(" ", "%20"); } return inUrl; } if (hasSpace) { // FIXME: Is this the correct place to add to searches? // what if someone else calls this function? int shortcut = parseUrlShortcut(inUrl); if (shortcut != SHORTCUT_INVALID) { Browser.addSearchUrl(mResolver, inUrl); String query = inUrl.substring(2); switch (shortcut) { case SHORTCUT_GOOGLE_SEARCH: return URLUtil.composeSearchUrl(query, QuickSearch_G, case SHORTCUT_WIKIPEDIA_SEARCH: return URLUtil.composeSearchUrl(query, QuickSearch_W, case SHORTCUT_DICTIONARY_SEARCH: return URLUtil.composeSearchUrl(query, QuickSearch_D, case SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH: // FIXME: we need location in this case return URLUtil.composeSearchUrl(query, QuickSearch_L, } } } else { if (Patterns.WEB_URL.matcher(inUrl).matches()) { return URLUtil.guessUrl(inUrl); } }

QUERY_PLACE_HOLDER); QUERY_PLACE_HOLDER); QUERY_PLACE_HOLDER);

QUERY_PLACE_HOLDER);

Browser.addSearchUrl(mResolver, inUrl); return URLUtil.composeSearchUrl(inUrl, QuickSearch_G, QUERY_PLACE_HOLDER); } /* package */ void setShouldShowErrorConsole(boolean flag) { if (flag == mShouldShowErrorConsole) { // Nothing to do.

return; } mShouldShowErrorConsole = flag; ErrorConsoleView errorConsole = mTabControl.getCurrentTab() .getErrorConsole(true); if (flag) { // Setting the show state of the console will cause it's the layout to be inflated. if (errorConsole.numberOfErrors() > 0) { errorConsole.showConsole(ErrorConsoleView.SHOW_MINIMIZED); } else { errorConsole.showConsole(ErrorConsoleView.SHOW_NONE); } // Now we can add it to the main view. mErrorConsoleContainer.addView(errorConsole, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); } else { mErrorConsoleContainer.removeView(errorConsole); } } boolean shouldShowErrorConsole() { return mShouldShowErrorConsole; } private void setStatusBarVisibility(boolean visible) { int flag = visible ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN; getWindow().setFlags(flag, WindowManager.LayoutParams.FLAG_FULLSCREEN); }

private void sendNetworkType(String type, String subtype) { WebView w = mTabControl.getCurrentWebView(); if (w != null) { w.setNetworkType(type, subtype); } } private void packageChanged(String packageName, boolean wasAdded) { WebView w = mTabControl.getCurrentWebView(); if (w == null) { return; } if (wasAdded) { w.addPackageName(packageName); } else { w.removePackageName(packageName); } } private void addPackageNames(Set<String> packageNames) { WebView w = mTabControl.getCurrentWebView(); if (w == null) { return; } w.addPackageNames(packageNames); } private void getInstalledPackages() { AsyncTask<Void, Void, Set<String> > task = new AsyncTask<Void, Void, Set<String> >() { protected Set<String> doInBackground(Void... unused) { Set<String> installedPackages = new HashSet<String>(); PackageManager pm = BrowserActivity.this.getPackageManager(); if (pm != null) { List<PackageInfo> packages = pm.getInstalledPackages(0); for (PackageInfo p : packages) { if (BrowserActivity.this.sGoogleApps.contains(p.packageName)) { installedPackages.add(p.packageName); } }

} return installedPackages; } // Executes on the UI thread protected void onPostExecute(Set<String> installedPackages) { addPackageNames(installedPackages); } }; task.execute(); } final static int LOCK_ICON_UNSECURE = 0; final static int LOCK_ICON_SECURE = 1; final static int LOCK_ICON_MIXED = 2; private private private private private private private BrowserSettings mSettings; TabControl mTabControl; ContentResolver mResolver; FrameLayout mContentView; View mCustomView; FrameLayout mCustomViewContainer; WebChromeClient.CustomViewCallback mCustomViewCallback;

// FIXME, temp address onPrepareMenu performance problem. When we move everything out of // view, we should rewrite this. private int mCurrentMenuState = 0; private int mMenuState = R.id.MAIN_MENU; private int mOldMenuState = EMPTY_MENU; private static final int EMPTY_MENU = -1; private Menu mMenu; private FindDialog mFindDialog; // Used to prevent chording to result in firing two shortcuts immediately // one after another. Fixes bug 1211714. boolean mCanChord; private boolean mInLoad; private boolean mIsNetworkUp; private boolean mDidStopLoad; /* package */ boolean mActivityInPause = true; private boolean mMenuIsDown; private static boolean mInTrace; // Performance probe private static final int[] SYSTEM_CPU_FORMAT = new int[] Process.PROC_SPACE_TERM | Process.PROC_COMBINE, Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG }; private private private private private private long long long long long long mStart; mProcessStart; mUserStart; mSystemStart; mIdleStart; mIrqStart;

{ // // // // // // // 1: 2: 3: 4: 5: 6: 7: user time nice time sys time idle time iowait time irq time softirq time

private long mUiStart; private Drawable private Drawable mMixLockIcon; mSecLockIcon;

/* hold a ref so we can auto-cancel if necessary */ private AlertDialog mAlertDialog; // The up-to-date URL and title (these can be different from those stored // in WebView, since it takes some time for the information in WebView to // get updated)

private String mUrl; private String mTitle; // As PageInfo has different style for landscape / portrait, we have // to re-open it when configuration changed private AlertDialog mPageInfoDialog; private Tab mPageInfoView; // If the Page-Info dialog is launched from the SSL-certificate-on-error // dialog, we should not just dismiss it, but should get back to the // SSL-certificate-on-error dialog. This flag is used to store this state private boolean mPageInfoFromShowSSLCertificateOnError; // as SSLCertificateOnError has different style for landscape / portrait, // we have to re-open it when configuration changed private AlertDialog mSSLCertificateOnErrorDialog; private WebView mSSLCertificateOnErrorView; private SslErrorHandler mSSLCertificateOnErrorHandler; private SslError mSSLCertificateOnErrorError; // as SSLCertificate has different style for landscape / portrait, we // have to re-open it when configuration changed private AlertDialog mSSLCertificateDialog; private Tab mSSLCertificateView; // as HttpAuthentication has different style for landscape / portrait, we // have to re-open it when configuration changed private AlertDialog mHttpAuthenticationDialog; private HttpAuthHandler mHttpAuthHandler; /*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); /*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER); // Google search final static String QuickSearch_G = "http://www.google.com/m?q=%s"; // Wikipedia search final static String QuickSearch_W = "http://en.wikipedia.org/w/index.php?search=%s&go=Go"; // Dictionary search final static String QuickSearch_D = "http://dictionary.reference.com/search?q=%s"; // Google Mobile Local search final static String QuickSearch_L = "http://www.google.com/m/search?site=local&q=%s&near=mountain+view"; final static String QUERY_PLACE_HOLDER = "%s"; // "source" parameter for Google search through search key final static String GOOGLE_SEARCH_SOURCE_SEARCHKEY = "browser-key"; // "source" parameter for Google search through goto menu final static String GOOGLE_SEARCH_SOURCE_GOTO = "browser-goto"; // "source" parameter for Google search through simplily type final static String GOOGLE_SEARCH_SOURCE_TYPE = "browser-type"; // "source" parameter for Google search suggested by the browser final static String GOOGLE_SEARCH_SOURCE_SUGGEST = "browser-suggest"; // "source" parameter for Google search from unknown source final static String GOOGLE_SEARCH_SOURCE_UNKNOWN = "unknown"; private final static String LOGTAG = "browser"; private String mLastEnteredUrl; private PowerManager.WakeLock mWakeLock; private final static int WAKELOCK_TIMEOUT = 5 * 60 * 1000; // 5 minutes private Toast mStopToast; private TitleBar mTitleBar; private LinearLayout mErrorConsoleContainer = null; private boolean mShouldShowErrorConsole = false; // As the ids are dynamically created, we can't guarantee that they will // be in sequence, so this static array maps ids to a window number. final static private int[] WINDOW_SHORTCUT_ID_ARRAY = { R.id.window_one_menu_id, R.id.window_two_menu_id, R.id.window_three_menu_id,

R.id.window_four_menu_id, R.id.window_five_menu_id, R.id.window_six_menu_id, R.id.window_seven_menu_id, R.id.window_eight_menu_id }; // monitor platform changes private IntentFilter mNetworkStateChangedFilter; private BroadcastReceiver mNetworkStateIntentReceiver; private BroadcastReceiver mPackageInstallationReceiver; private SystemAllowGeolocationOrigins mSystemAllowGeolocationOrigins; // activity requestCode final static int COMBO_PAGE final static int DOWNLOAD_PAGE final static int PREFERENCES_PAGE final static int FILE_SELECTED // the default <video> poster private Bitmap mDefaultVideoPoster; // the video progress view private View mVideoProgressView; // The Google packages we monitor for the navigator.isApplicationInstalled() // API. Add as needed. private static Set<String> sGoogleApps; static { sGoogleApps = new HashSet<String>(); sGoogleApps.add("com.google.android.youtube"); } /** * A UrlData class to abstract how the content will be set to WebView. * This base class uses loadUrl to show the content. */ /* package */ static class UrlData { final String mUrl; final Map<String, String> mHeaders; final Intent mVoiceIntent; UrlData(String url) { this.mUrl = url; this.mHeaders = null; this.mVoiceIntent = null; } UrlData(String url, Map<String, String> headers, Intent intent) { this.mUrl = url; this.mHeaders = headers; if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS .equals(intent.getAction())) { this.mVoiceIntent = intent; } else { this.mVoiceIntent = null; } } boolean isEmpty() { return mVoiceIntent == null && (mUrl == null || mUrl.length() == 0); } /** * Load this UrlData into the given Tab. Use loadUrlDataIn to update * the title bar as well. */ public void loadIn(Tab t) { if (mVoiceIntent != null) { t.activateVoiceSearchMode(mVoiceIntent); } else { t.getWebView().loadUrl(mUrl, mHeaders); } } }; /* package */ static final UrlData EMPTY_URL_DATA = new UrlData(null); }

= = = =

1; 2; 3; 4;

ATTACHMENT H

/* * * * * * * * * * * * * * * */

Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You 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.

// BEGIN android-note // Completely different implementation from harmony. // BEGIN android-note package java.util; import import import import import import java.io.IOException; java.io.InvalidObjectException; java.io.ObjectInputStream; java.io.ObjectOutputStream; java.io.ObjectStreamField; java.io.Serializable;

Runs much faster.

/** * HashMap is an implementation of {@link Map}. All optional operations are supported. * * <p>All elements are permitted as keys or values, including null. * * <p>Note that the iteration order for HashMap is non-deterministic. If you want * deterministic iteration, use {@link LinkedHashMap}. * * <p>Note: the implementation of {@code HashMap} is not synchronized. * If one thread of several threads accessing an instance modifies the map * structurally, access to the map needs to be synchronized. A structural * modification is an operation that adds or removes an entry. Changes in * the value of an entry are not structural changes. * * <p>The {@code Iterator} created by calling the {@code iterator} method * may throw a {@code ConcurrentModificationException} if the map is structurally * changed while an iterator is used to iterate over the elements. Only the * {@code remove} method that is provided by the iterator allows for removal of * elements during iteration. It is not possible to guarantee that this * mechanism works in all cases of unsynchronized concurrent modification. It * should only be used for debugging purposes. * * @param <K> the type of keys maintained by this map * @param <V> the type of mapped values */ public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable { /** * Min capacity (other than zero) for a HashMap. Must be a power of two * greater than 1 (and less than 1 << 30). */ private static final int MINIMUM_CAPACITY = 4; /** * Max capacity for a HashMap. Must be a power of two >= MINIMUM_CAPACITY.

*/ private static final int MAXIMUM_CAPACITY = 1 << 30; /** * An empty table shared by all zero-capacity maps (typically from default * constructor). It is never written to, and replaced on first put. Its size * is set to half the minimum, so that the first resize will create a * minimum-sized table. */ private static final Entry[] EMPTY_TABLE = new HashMapEntry[MINIMUM_CAPACITY >>> 1]; /** * The default load factor. Note that this implementation ignores the * load factor, but cannot do away with it entirely because it's * mentioned in the API. * * <p>Note that this constant has no impact on the behavior of the program, * but it is emitted as part of the serialized form. The load factor of * .75 is hardwired into the program, which uses cheap shifts in place of * expensive division. */ static final float DEFAULT_LOAD_FACTOR = .75F; /** * The hash table. If this hash map contains a mapping for null, it is * not represented this hash table. */ transient HashMapEntry<K, V>[] table; /** * The entry representing the null key, or null if there's no such mapping. */ transient HashMapEntry<K, V> entryForNullKey; /** * The number of mappings in this hash map. */ transient int size; /** * Incremented by "structural modifications" to allow (best effort) * detection of concurrent modification. */ transient int modCount; /** * The table is rehashed when its size exceeds this threshold. * The value of this field is generally .75 * capacity, except when * the capacity is zero, as described in the EMPTY_TABLE declaration * above. */ private transient int threshold; // Views - lazily private transient private transient private transient initialized Set<K> keySet; Set<Entry<K, V>> entrySet; Collection<V> values;

/** * Constructs a new empty {@code HashMap} instance. */ @SuppressWarnings("unchecked") public HashMap() {

table = (HashMapEntry<K, V>[]) EMPTY_TABLE; threshold = -1; // Forces first put invocation to replace EMPTY_TABLE } /** * Constructs a new {@code HashMap} instance with the specified capacity. * * @param capacity * the initial capacity of this hash map. * @throws IllegalArgumentException * when the capacity is less than zero. */ public HashMap(int capacity) { if (capacity < 0) { throw new IllegalArgumentException("Capacity: " + capacity); } if (capacity == 0) { @SuppressWarnings("unchecked") HashMapEntry<K, V>[] tab = (HashMapEntry<K, V>[]) EMPTY_TABLE; table = tab; threshold = -1; // Forces first put() to replace EMPTY_TABLE return; } if (capacity < MINIMUM_CAPACITY) { capacity = MINIMUM_CAPACITY; } else if (capacity > MAXIMUM_CAPACITY) { capacity = MAXIMUM_CAPACITY; } else { capacity = roundUpToPowerOfTwo(capacity); } makeTable(capacity); } /** * Constructs a new {@code HashMap} instance with the specified capacity and * load factor. * * @param capacity * the initial capacity of this hash map. * @param loadFactor * the initial load factor. * @throws IllegalArgumentException * when the capacity is less than zero or the load factor is * less or equal to zero or NaN. */ public HashMap(int capacity, float loadFactor) { this(capacity); if (loadFactor <= 0 || Float.isNaN(loadFactor)) { throw new IllegalArgumentException("Load factor: " + loadFactor); } /* * Note that this implementation ignores loadFactor; it always uses * a load factor of 3/4. This simplifies the code and generally * improves performance. */ } /** * Constructs a new {@code HashMap} instance containing the mappings from * the specified map.

* * @param map * the mappings to add. */ public HashMap(Map<? extends K, ? extends V> map) { this(capacityForInitSize(map.size())); constructorPutAll(map); } /** * Inserts all of the elements of map into this HashMap in a manner * suitable for use by constructors and pseudo-constructors (i.e., clone, * readObject). Also used by LinkedHashMap. */ final void constructorPutAll(Map<? extends K, ? extends V> map) { for (Entry<? extends K, ? extends V> e : map.entrySet()) { constructorPut(e.getKey(), e.getValue()); } } /** * Returns an appropriate capacity for the specified initial size. Does * not round the result up to a power of two; the caller must do this! * The returned value will be between 0 and MAXIMUM_CAPACITY (inclusive). */ static int capacityForInitSize(int size) { int result = (size >> 1) + size; // Multiply by 3/2 to allow for growth // boolean expr is equivalent to result >= 0 && result<MAXIMUM_CAPACITY return (result & ~(MAXIMUM_CAPACITY-1))==0 ? result : MAXIMUM_CAPACITY; } /** * Returns a shallow copy of this map. * * @return a shallow copy of this map. */ @SuppressWarnings("unchecked") @Override public Object clone() { /* * This could be made more efficient. It unnecessarily hashes all of * the elements in the map. */ HashMap<K, V> result; try { result = (HashMap<K, V>) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(e); } // Restore clone to empty state, retaining our capacity and threshold result.makeTable(table.length); result.entryForNullKey = null; result.size = 0; result.keySet = null; result.entrySet = null; result.values = null; result.init(); // Give subclass a chance to initialize itself result.constructorPutAll(this); // Calls method overridden in subclass!! return result; } /**

* This method is called from the pseudo-constructors (clone and readObject) * prior to invoking constructorPut/constructorPutAll, which invoke the * overridden constructorNewEntry method. Normally it is a VERY bad idea to * invoke an overridden method from a pseudo-constructor (Effective Java * Item 17). In this cases it is unavoidable, and the init method provides a * workaround. */ void init() { } /** * Returns whether this map is empty. * * @return {@code true} if this map has no elements, {@code false} * otherwise. * @see #size() */ @Override public boolean isEmpty() { return size == 0; } /** * Returns the number of elements in this map. * * @return the number of elements in this map. */ @Override public int size() { return size; } /** * Returns the value of the mapping with the specified key. * * @param key * the key. * @return the value of the mapping with the specified key, or {@code null} * if no mapping for the specified key is found. */ public V get(Object key) { if (key == null) { HashMapEntry<K, V> e = entryForNullKey; return e == null ? null : e.value; } // Doug Lea's supplemental secondaryHash function (inlined) int hash = key.hashCode(); hash ^= (hash >>> 20) ^ (hash >>> 12); hash ^= (hash >>> 7) ^ (hash >>> 4); HashMapEntry<K, V>[] tab = table; for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { return e.value; } } return null; } /** * Returns whether this map contains the specified key. * * @param key * the key to search for.

* @return {@code true} if this map contains the specified key, * {@code false} otherwise. */ @Override public boolean containsKey(Object key) { if (key == null) { return entryForNullKey != null; } // Doug Lea's supplemental secondaryHash function (inlined) int hash = key.hashCode(); hash ^= (hash >>> 20) ^ (hash >>> 12); hash ^= (hash >>> 7) ^ (hash >>> 4); HashMapEntry<K, V>[] tab = table; for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { return true; } } return false; } /** * Returns whether this map contains the specified value. * * @param value * the value to search for. * @return {@code true} if this map contains the specified value, * {@code false} otherwise. */ @Override public boolean containsValue(Object value) { HashMapEntry[] tab = table; int len = tab.length; if (value == null) { for (int i = 0; i < len; i++) { for (HashMapEntry e = tab[i]; e != null; e = e.next) { if (e.value == null) { return true; } } } return entryForNullKey != null && entryForNullKey.value == null; } // value is non-null for (int i = 0; i < len; i++) { for (HashMapEntry e = tab[i]; e != null; e = e.next) { if (value.equals(e.value)) { return true; } } } return entryForNullKey != null && value.equals(entryForNullKey.value); } /** * Maps the specified key to the specified value. * * @param key * the key. * @param value * the value.

* @return the value of any previous mapping with the specified key or * {@code null} if there was no such mapping. */ @Override public V put(K key, V value) { if (key == null) { return putValueForNullKey(value); } int hash = secondaryHash(key.hashCode()); HashMapEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { preModify(e); V oldValue = e.value; e.value = value; return oldValue; } } // No entry for (non-null) key is present; create one modCount++; if (size++ > threshold) { tab = doubleCapacity(); index = hash & (tab.length - 1); } addNewEntry(key, value, hash, index); return null; } private V putValueForNullKey(V value) { HashMapEntry<K, V> entry = entryForNullKey; if (entry == null) { addNewEntryForNullKey(value); size++; modCount++; return null; } else { preModify(entry); V oldValue = entry.value; entry.value = value; return oldValue; } } /** * Give LinkedHashMap a chance to take action when we modify an existing * entry. * * @param e the entry we're about to modify. */ void preModify(HashMapEntry<K, V> e) { } /** * This method is just like put, except that it doesn't do things that * are inappropriate or unnecessary for constructors and pseudo-constructors * (i.e., clone, readObject). In particular, this method does not check to * ensure that capacity is sufficient, and does not increment modCount. */ private void constructorPut(K key, V value) { if (key == null) { HashMapEntry<K, V> entry = entryForNullKey; if (entry == null) { entryForNullKey = constructorNewEntry(null, value, 0, null);

size++; } else { entry.value = value; } return; } int hash = secondaryHash(key.hashCode()); HashMapEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); HashMapEntry<K, V> first = tab[index]; for (HashMapEntry<K, V> e = first; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { e.value = value; return; } } // No entry for (non-null) key is present; create one tab[index] = constructorNewEntry(key, value, hash, first); size++; } /** * Creates a new entry for the given key, value, hash, and index and * inserts it into the hash table. This method is called by put * (and indirectly, putAll), and overridden by LinkedHashMap. The hash * must incorporate the secondary hash function. */ void addNewEntry(K key, V value, int hash, int index) { table[index] = new HashMapEntry<K, V>(key, value, hash, table[index]); } /** * Creates a new entry for the null key, and the given value and * inserts it into the hash table. This method is called by put * (and indirectly, putAll), and overridden by LinkedHashMap. */ void addNewEntryForNullKey(V value) { entryForNullKey = new HashMapEntry<K, V>(null, value, 0, null); } /** * Like newEntry, but does not perform any activity that would be * unnecessary or inappropriate for constructors. In this class, the * two methods behave identically; in LinkedHashMap, they differ. */ HashMapEntry<K, V> constructorNewEntry( K key, V value, int hash, HashMapEntry<K, V> first) { return new HashMapEntry<K, V>(key, value, hash, first); } /** * Copies all the mappings in the specified map to this map. These mappings * will replace all mappings that this map had for any of the keys currently * in the given map. * * @param map * the map to copy mappings from. */ @Override public void putAll(Map<? extends K, ? extends V> map) { ensureCapacity(map.size()); super.putAll(map); }

/** * Ensures that the hash table has sufficient capacity to store the * specified number of mappings, with room to grow. If not, it increases the * capacity as appropriate. Like doubleCapacity, this method moves existing * entries to new buckets as appropriate. Unlike doubleCapacity, this method * can grow the table by factors of 2^n for n > 1. Hopefully, a single call * to this method will be faster than multiple calls to doubleCapacity. * * <p>This method is called only by putAll. */ private void ensureCapacity(int numMappings) { int newCapacity = roundUpToPowerOfTwo(capacityForInitSize(numMappings)); HashMapEntry<K, V>[] oldTable = table; int oldCapacity = oldTable.length; if (newCapacity <= oldCapacity) { return; } if (newCapacity == oldCapacity << 1) { doubleCapacity(); return; } // We're growing by at least 4x, rehash in the obvious way HashMapEntry<K, V>[] newTable = makeTable(newCapacity); if (size != 0) { int newMask = newCapacity - 1; for (int i = 0; i < oldCapacity; i++) { for (HashMapEntry<K, V> e = oldTable[i]; e != null;) { HashMapEntry<K, V> oldNext = e.next; int newIndex = e.hash & newMask; HashMapEntry<K, V> newNext = newTable[newIndex]; newTable[newIndex] = e; e.next = newNext; e = oldNext; } } } } /** * Allocate a table of the given capacity and set the threshold accordingly. * @param newCapacity must be a power of two */ private HashMapEntry<K, V>[] makeTable(int newCapacity) { @SuppressWarnings("unchecked") HashMapEntry<K, V>[] newTable = (HashMapEntry<K, V>[]) new HashMapEntry[newCapacity]; table = newTable; threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity return newTable; } /** * Doubles the capacity of the hash table. Existing entries are placed in * the correct bucket on the enlarged table. If the current capacity is, * MAXIMUM_CAPACITY, this method is a no-op. Returns the table, which * will be new unless we were already at MAXIMUM_CAPACITY. */ private HashMapEntry<K, V>[] doubleCapacity() { HashMapEntry<K, V>[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { return oldTable; }

int newCapacity = oldCapacity << 1; HashMapEntry<K, V>[] newTable = makeTable(newCapacity); if (size == 0) { return newTable; } for (int j = 0; j < oldCapacity; j++) { /* * Rehash the bucket using the minimum number of field writes. * This is the most subtle and delicate code in the class. */ HashMapEntry<K, V> e = oldTable[j]; if (e == null) { continue; } int highBit = e.hash & oldCapacity; HashMapEntry<K, V> broken = null; newTable[j | highBit] = e; for (HashMapEntry<K, V> n = e.next; n != null; e = n, n = n.next) { int nextHighBit = n.hash & oldCapacity; if (nextHighBit != highBit) { if (broken == null) newTable[j | nextHighBit] = n; else broken.next = n; broken = e; highBit = nextHighBit; } } if (broken != null) broken.next = null; } return newTable; } /** * Removes the mapping with the specified key from this map. * * @param key * the key of the mapping to remove. * @return the value of the removed mapping or {@code null} if no mapping * for the specified key was found. */ @Override public V remove(Object key) { if (key == null) { return removeNullKey(); } int hash = secondaryHash(key.hashCode()); HashMapEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); for (HashMapEntry<K, V> e = tab[index], prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && key.equals(e.key)) { if (prev == null) { tab[index] = e.next; } else { prev.next = e.next; } modCount++; size--; postRemove(e); return e.value; } }

return null; } private V removeNullKey() { HashMapEntry<K, V> e = entryForNullKey; if (e == null) { return null; } entryForNullKey = null; modCount++; size--; postRemove(e); return e.value; } /** * Subclass overrides this method to unlink entry. */ void postRemove(HashMapEntry<K, V> e) { } /** * Removes all mappings from this hash map, leaving it empty. * * @see #isEmpty * @see #size */ @Override public void clear() { if (size != 0) { Arrays.fill(table, null); entryForNullKey = null; modCount++; size = 0; } } /** * Returns a set of the keys contained in this map. The set is backed by * this map so changes to one are reflected by the other. The set does not * support adding. * * @return a set of the keys. */ @Override public Set<K> keySet() { Set<K> ks = keySet; return (ks != null) ? ks : (keySet = new KeySet()); } /** * Returns a collection of the values contained in this map. The collection * is backed by this map so changes to one are reflected by the other. The * collection supports remove, removeAll, retainAll and clear operations, * and it does not support add or addAll operations. * <p> * This method returns a collection which is the subclass of * AbstractCollection. The iterator method of this subclass returns a * "wrapper object" over the iterator of map's entrySet(). The {@code size} * method wraps the map's size method and the {@code contains} method wraps * the map's containsValue method. * </p> * <p> * The collection is created when this method is called for the first time * and returned in response to all subsequent calls. This method may return * different collections when multiple concurrent calls occur, since no * synchronization is performed.

* </p> * * @return a collection of the values contained in this map. */ @Override public Collection<V> values() { Collection<V> vs = values; return (vs != null) ? vs : (values = new Values()); } /** * Returns a set containing all of the mappings in this map. Each mapping is * an instance of {@link Map.Entry}. As the set is backed by this map, * changes in one will be reflected in the other. * * @return a set of the mappings. */ public Set<Entry<K, V>> entrySet() { Set<Entry<K, V>> es = entrySet; return (es != null) ? es : (entrySet = new EntrySet()); } static class HashMapEntry<K, V> implements Entry<K, V> { final K key; V value; final int hash; HashMapEntry<K, V> next; HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) { this.key = key; this.value = value; this.hash = hash; this.next = next; } public final K getKey() { return key; } public final V getValue() { return value; } public final V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } @Override public final boolean equals(Object o) { if (!(o instanceof Entry)) { return false; } Entry<?, ?> e = (Entry<?, ?>) o; return HashMap.equals(e.getKey(), key) && HashMap.equals(e.getValue(), value); } @Override public final int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } @Override public final String toString() { return key + "=" + value;

} } private abstract class HashIterator { int nextIndex; HashMapEntry<K, V> nextEntry = entryForNullKey; HashMapEntry<K, V> lastEntryReturned; int expectedModCount = modCount; HashIterator() { if (nextEntry == null) { HashMapEntry<K, V>[] tab = table; HashMapEntry<K, V> next = null; while (next == null && nextIndex < tab.length) { next = tab[nextIndex++]; } nextEntry = next; } } public boolean hasNext() { return nextEntry != null; } HashMapEntry<K, V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (nextEntry == null) throw new NoSuchElementException(); HashMapEntry<K, V> entryToReturn = nextEntry; HashMapEntry<K, V>[] tab = table; HashMapEntry<K, V> next = entryToReturn.next; while (next == null && nextIndex < tab.length) { next = tab[nextIndex++]; } nextEntry = next; return lastEntryReturned = entryToReturn; } public void remove() { if (lastEntryReturned == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); HashMap.this.remove(lastEntryReturned.key); lastEntryReturned = null; expectedModCount = modCount; } } private final class KeyIterator extends HashIterator implements Iterator<K> { public K next() { return nextEntry().key; } } private final class ValueIterator extends HashIterator implements Iterator<V> { public V next() { return nextEntry().value; } } private final class EntryIterator extends HashIterator implements Iterator<Entry<K, V>> { public Entry<K, V> next() { return nextEntry(); }

} /** * Returns true if this map contains the specified mapping. */ private boolean containsMapping(Object key, Object value) { if (key == null) { HashMapEntry<K, V> e = entryForNullKey; return e != null && equals(value, e.value); } int hash = secondaryHash(key.hashCode()); HashMapEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { return equals(value, e.value); } } return false; // No entry for key } /** * Removes the mapping from key to value and returns true if this mapping * exists; otherwise, returns does nothing and returns false. */ private boolean removeMapping(Object key, Object value) { if (key == null) { HashMapEntry<K, V> e = entryForNullKey; if (e == null || !equals(value, e.value)) { return false; } entryForNullKey = null; modCount++; size--; postRemove(e); return true; } int hash = secondaryHash(key.hashCode()); HashMapEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); for (HashMapEntry<K, V> e = tab[index], prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && key.equals(e.key)) { if (!equals(value, e.value)) { return false; // Map has wrong value for key } if (prev == null) { tab[index] = e.next; } else { prev.next = e.next; } modCount++; size--; postRemove(e); return true; } } return false; // No entry for key } private static boolean equals(Object o1, Object o2) { return o1 == o2 || (o1 != null && o1.equals(o2));

} // Subclass (LinkedHashMap) overrides these for correct iteration order Iterator<K> newKeyIterator() { return new KeyIterator(); } Iterator<V> newValueIterator() { return new ValueIterator(); } Iterator<Entry<K, V>> newEntryIterator() { return new EntryIterator(); } private final class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return newKeyIterator(); } public int size() { return size; } public boolean isEmpty() { return size == 0; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { int oldSize = size; HashMap.this.remove(o); return size != oldSize; } public void clear() { HashMap.this.clear(); } } private final class Values extends AbstractCollection<V> { public Iterator<V> iterator() { return newValueIterator(); } public int size() { return size; } public boolean isEmpty() { return size == 0; } public boolean contains(Object o) { return containsValue(o); } public void clear() { HashMap.this.clear(); } } private final class EntrySet extends AbstractSet<Entry<K, V>> { public Iterator<Entry<K, V>> iterator() { return newEntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Entry)) return false; Entry<?, ?> e = (Entry<?, ?>) o; return containsMapping(e.getKey(), e.getValue()); } public boolean remove(Object o) { if (!(o instanceof Entry)) return false; Entry<?, ?> e = (Entry<?, ?>)o; return removeMapping(e.getKey(), e.getValue()); }

public int size() { return size; } public boolean isEmpty() { return size == 0; } public void clear() { HashMap.this.clear(); } } /** * Applies a supplemental hash function to a given hashCode, which defends * against poor quality hash functions. This is critical because HashMap * uses power-of-two length hash tables, that otherwise encounter collisions * for hashCodes that do not differ in lower or upper bits. */ private static int secondaryHash(int h) { // Doug Lea's supplemental hash function h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } /** * Returns the smallest power of two >= its argument, with several caveats: * If the argument is negative but not Integer.MIN_VALUE, the method returns * zero. If the argument is > 2^30 or equal to Integer.MIN_VALUE, the method * returns Integer.MIN_VALUE. If the argument is zero, the method returns * zero. */ private static int roundUpToPowerOfTwo(int i) { i--; // If input is a power of two, shift its high-order bit right // "Smear" the high-order bit all the way to the right i |= i >>> 1; i |= i >>> 2; i |= i >>> 4; i |= i >>> 8; i |= i >>> 16; return i + 1; } private static final long serialVersionUID = 362498820763181265L; /** * Serializable fields. * * @serialField loadFactor float * load factor for this HashMap */ private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("loadFactor", Float.TYPE) }; private void writeObject(ObjectOutputStream stream) throws IOException { // Emulate loadFactor field for other implementations to read ObjectOutputStream.PutField fields = stream.putFields(); fields.put("loadFactor", DEFAULT_LOAD_FACTOR); stream.writeFields(); stream.writeInt(table.length); // Capacity stream.writeInt(size); for (Entry<K, V> e : entrySet()) {

stream.writeObject(e.getKey()); stream.writeObject(e.getValue()); } } private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int capacity = stream.readInt(); if (capacity < 0) { throw new InvalidObjectException("Capacity: " + capacity); } if (capacity < MINIMUM_CAPACITY) { capacity = MINIMUM_CAPACITY; } else if (capacity > MAXIMUM_CAPACITY) { capacity = MAXIMUM_CAPACITY; } else { capacity = roundUpToPowerOfTwo(capacity); } makeTable(capacity); int size = stream.readInt(); if (size < 0) { throw new InvalidObjectException("Size: " + size); } init(); // Give subclass (LinkedHashMap) a chance to initialize itself for (int i = 0; i < size; i++) { @SuppressWarnings("unchecked") K key = (K) stream.readObject(); @SuppressWarnings("unchecked") V val = (V) stream.readObject(); constructorPut(key, val); } } }

ATTACHMENT I

/* * 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 com.android.server; import import import import import import import import import import import import import import import import import import java.io.PrintWriter; java.util.ArrayList; java.util.Collections; java.util.Comparator; java.util.HashMap; java.util.HashSet; java.util.Iterator; java.util.List; java.util.Map; java.util.Set; android.util.Log; android.util.Slog; android.util.LogPrinter; android.util.Printer; android.util.Config; android.content.ContentResolver; android.content.Intent; android.content.IntentFilter;

/** * {@hide} */ public class IntentResolver<F extends IntentFilter, R extends Object> { final private static String TAG = "IntentResolver"; final private static boolean DEBUG = false; final private static boolean localLOGV = DEBUG || Config.LOGV; public void addFilter(F f) { if (localLOGV) { Slog.v(TAG, "Adding filter: " + f); f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " Slog.v(TAG, " Building Lookup Maps:"); } mFilters.add(f); int numS = register_intent_filter(f, f.schemesIterator(), mSchemeToFilter, " Scheme: "); int numT = register_mime_types(f, " Type: "); if (numS == 0 && numT == 0) { register_intent_filter(f, f.actionsIterator(), mActionToFilter, " Action: "); } if (numT != 0) { register_intent_filter(f, f.actionsIterator(), mTypedActionToFilter, " TypedAction: "); } } public void removeFilter(F f) { removeFilterInternal(f); mFilters.remove(f);

");

} void removeFilterInternal(F f) { if (localLOGV) { Slog.v(TAG, "Removing filter: " + f); f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " Slog.v(TAG, " Cleaning Lookup Maps:"); } int numS = unregister_intent_filter(f, f.schemesIterator(), mSchemeToFilter, " Scheme: "); int numT = unregister_mime_types(f, " Type: "); if (numS == 0 && numT == 0) { unregister_intent_filter(f, f.actionsIterator(), mActionToFilter, " Action: "); } if (numT != 0) { unregister_intent_filter(f, f.actionsIterator(), mTypedActionToFilter, " TypedAction: "); } } boolean dumpMap(PrintWriter out, String titlePrefix, String title, String prefix, Map<String, ArrayList<F>> map, String packageName) { String eprefix = prefix + " "; String fprefix = prefix + " "; boolean printedSomething = false; for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) { ArrayList<F> a = e.getValue(); final int N = a.size(); boolean printedHeader = false; for (int i=0; i<N; i++) { F filter = a.get(i); if (packageName != null && !packageName.equals(packageForFilter(filter))) { continue; } if (title != null) { out.print(titlePrefix); out.println(title); title = null; } if (!printedHeader) { out.print(eprefix); out.print(e.getKey()); out.println(":"); printedHeader = true; } printedSomething = true; dumpFilter(out, fprefix, filter); } } return printedSomething; } public boolean dump(PrintWriter out, String title, String prefix, String packageName) { String innerPrefix = prefix + " "; String sepPrefix = "\n" + prefix; String curPrefix = title + "\n" + prefix; if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix, mTypeToFilter, packageName)) { curPrefix = sepPrefix; } if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix, mBaseTypeToFilter, packageName)) { curPrefix = sepPrefix; } if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix, mWildTypeToFilter, packageName)) { curPrefix = sepPrefix; } if (dumpMap(out, curPrefix, "Schemes:", innerPrefix, mSchemeToFilter, packageName)) { curPrefix = sepPrefix; }

");

if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix, mActionToFilter, packageName)) { curPrefix = sepPrefix; } if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix, mTypedActionToFilter, packageName)) { curPrefix = sepPrefix; } return curPrefix == sepPrefix; } private class IteratorWrapper implements Iterator<F> { private final Iterator<F> mI; private F mCur; IteratorWrapper(Iterator<F> it) { mI = it; } public boolean hasNext() { return mI.hasNext(); } public F next() { return (mCur = mI.next()); } public void remove() { if (mCur != null) { removeFilterInternal(mCur); } mI.remove(); } } /** * Returns an iterator allowing filters to be removed. */ public Iterator<F> filterIterator() { return new IteratorWrapper(mFilters.iterator()); } /** * Returns a read-only set of the filters. */ public Set<F> filterSet() { return Collections.unmodifiableSet(mFilters); } public List<R> queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly, ArrayList<ArrayList<F>> listCut) { ArrayList<R> resultList = new ArrayList<R>(); final boolean debug = localLOGV || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); final String scheme = intent.getScheme(); int N = listCut.size(); for (int i = 0; i < N; ++i) { buildResolveList(intent, debug, defaultOnly, resolvedType, scheme, listCut.get(i), resultList); } sortResults(resultList); return resultList; } public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { String scheme = intent.getScheme(); ArrayList<R> finalList = new ArrayList<R>();

final boolean debug = localLOGV || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); if (debug) Slog.v( TAG, "Resolving type " + resolvedType + " scheme " + scheme + " of intent " + intent); ArrayList<F> ArrayList<F> ArrayList<F> ArrayList<F> firstTypeCut = null; secondTypeCut = null; thirdTypeCut = null; schemeCut = null;

// If the intent includes a MIME type, then we want to collect all of // the filters that match that MIME type. if (resolvedType != null) { int slashpos = resolvedType.indexOf('/'); if (slashpos > 0) { final String baseType = resolvedType.substring(0, slashpos); if (!baseType.equals("*")) { if (resolvedType.length() != slashpos+2 || resolvedType.charAt(slashpos+1) != '*') { // Not a wild card, so we can just look for all filters that // completely match or wildcards whose base type matches. firstTypeCut = mTypeToFilter.get(resolvedType); if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut); secondTypeCut = mWildTypeToFilter.get(baseType); if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut); } else { // We can match anything with our base type. firstTypeCut = mBaseTypeToFilter.get(baseType); if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut); secondTypeCut = mWildTypeToFilter.get(baseType); if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut); } // Any */* types always apply, but we only need to do this // if the intent type was not already */*. thirdTypeCut = mWildTypeToFilter.get("*"); if (debug) Slog.v(TAG, "Third type cut: " + thirdTypeCut); } else if (intent.getAction() != null) { // The intent specified any type ({@literal *}/*). This // can be a whole heck of a lot of things, so as a first // cut let's use the action instead. firstTypeCut = mTypedActionToFilter.get(intent.getAction()); if (debug) Slog.v(TAG, "Typed Action list: " + firstTypeCut); } } } // // // if If the intent includes a data URI, then we want to collect all of the filters that match its scheme (we will further refine matches on the authority and path by directly matching each resulting filter). (scheme != null) { schemeCut = mSchemeToFilter.get(scheme); if (debug) Slog.v(TAG, "Scheme list: " + schemeCut);

} // // // if If the intent does not specify any data -- either a MIME type or a URI -- then we will only be looking for matches against empty data. (resolvedType == null && scheme == null && intent.getAction() != null) { firstTypeCut = mActionToFilter.get(intent.getAction()); if (debug) Slog.v(TAG, "Action list: " + firstTypeCut);

} if (firstTypeCut != null) { buildResolveList(intent, debug, defaultOnly, resolvedType, scheme, firstTypeCut, finalList); } if (secondTypeCut != null) { buildResolveList(intent, debug, defaultOnly,

resolvedType, scheme, secondTypeCut, finalList); } if (thirdTypeCut != null) { buildResolveList(intent, debug, defaultOnly, resolvedType, scheme, thirdTypeCut, finalList); } if (schemeCut != null) { buildResolveList(intent, debug, defaultOnly, resolvedType, scheme, schemeCut, finalList); } sortResults(finalList); if (debug) { Slog.v(TAG, "Final result list:"); for (R r : finalList) { Slog.v(TAG, " " + r); } } return finalList; } /** * Control whether the given filter is allowed to go into the result * list. Mainly intended to prevent adding multiple filters for the * same target object. */ protected boolean allowFilterResult(F filter, List<R> dest) { return true; } protected String packageForFilter(F filter) { return null; } protected R newResult(F filter, int match) { return (R)filter; } protected void sortResults(List<R> results) { Collections.sort(results, mResolvePrioritySorter); } protected void dumpFilter(PrintWriter out, String prefix, F filter) { out.print(prefix); out.println(filter); } private final int register_mime_types(F filter, String prefix) { final Iterator<String> i = filter.typesIterator(); if (i == null) { return 0; } int num = 0; while (i.hasNext()) { String name = (String)i.next(); num++; if (localLOGV) Slog.v(TAG, prefix + name); String baseName = name; final int slashpos = name.indexOf('/'); if (slashpos > 0) { baseName = name.substring(0, slashpos).intern(); } else { name = name + "/*"; } ArrayList<F> array = mTypeToFilter.get(name); if (array == null) { //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); mTypeToFilter.put(name, array); }

array.add(filter); if (slashpos > 0) { array = mBaseTypeToFilter.get(baseName); if (array == null) { //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); mBaseTypeToFilter.put(baseName, array); } array.add(filter); } else { array = mWildTypeToFilter.get(baseName); if (array == null) { //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); mWildTypeToFilter.put(baseName, array); } array.add(filter); } } return num; } private final int unregister_mime_types(F filter, String prefix) { final Iterator<String> i = filter.typesIterator(); if (i == null) { return 0; } int num = 0; while (i.hasNext()) { String name = (String)i.next(); num++; if (localLOGV) Slog.v(TAG, prefix + name); String baseName = name; final int slashpos = name.indexOf('/'); if (slashpos > 0) { baseName = name.substring(0, slashpos).intern(); } else { name = name + "/*"; } if (!remove_all_objects(mTypeToFilter.get(name), filter)) { mTypeToFilter.remove(name); } if (slashpos > 0) { if (!remove_all_objects(mBaseTypeToFilter.get(baseName), filter)) { mBaseTypeToFilter.remove(baseName); } } else { if (!remove_all_objects(mWildTypeToFilter.get(baseName), filter)) { mWildTypeToFilter.remove(baseName); } } } return num; } private final int register_intent_filter(F filter, Iterator<String> i, HashMap<String, ArrayList<F>> dest, String prefix) { if (i == null) { return 0; } int num = 0; while (i.hasNext()) { String name = i.next(); num++; if (localLOGV) Slog.v(TAG, prefix + name);

ArrayList<F> array = dest.get(name); if (array == null) { //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); dest.put(name, array); } array.add(filter); } return num; } private final int unregister_intent_filter(F filter, Iterator<String> i, HashMap<String, ArrayList<F>> dest, String prefix) { if (i == null) { return 0; } int num = 0; while (i.hasNext()) { String name = i.next(); num++; if (localLOGV) Slog.v(TAG, prefix + name); if (!remove_all_objects(dest.get(name), filter)) { dest.remove(name); } } return num; } private final boolean remove_all_objects(List<F> list, Object object) { if (list != null) { int N = list.size(); for (int idx=0; idx<N; idx++) { if (list.get(idx) == object) { list.remove(idx); idx--; N--; } } return N > 0; } return false; } private void buildResolveList(Intent intent, boolean debug, boolean defaultOnly, String resolvedType, String scheme, List<F> src, List<R> dest) { Set<String> categories = intent.getCategories(); final int N = src != null ? src.size() : 0; boolean hasNonDefaults = false; int i; for (i=0; i<N; i++) { F filter = src.get(i); int match; if (debug) Slog.v(TAG, "Matching against filter " + filter); // Do we already have this one? if (!allowFilterResult(filter, dest)) { if (debug) { Slog.v(TAG, " Filter's target already added"); } continue; } match = filter.match( intent.getAction(), resolvedType, scheme, intent.getData(), categories, TAG); if (match >= 0) { if (debug) Slog.v(TAG, " Filter matched! match=0x" + Integer.toHexString(match)); if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) { final R oneResult = newResult(filter, match);

if (oneResult != null) { dest.add(oneResult); } } else { hasNonDefaults = true; } } else { if (debug) { String reason; switch (match) { case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; case IntentFilter.NO_MATCH_DATA: reason = "data"; break; case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; default: reason = "unknown reason"; break; } Slog.v(TAG, " Filter did not match: " + reason); } } } if (dest.size() == 0 && hasNonDefaults) { Slog.w(TAG, "resolveIntent failed: found match, but none with Intent.CATEGORY_DEFAULT"); } } // Sorts a List of IntentFilter objects into descending priority order. private static final Comparator mResolvePrioritySorter = new Comparator() { public int compare(Object o1, Object o2) { float q1 = ((IntentFilter)o1).getPriority(); float q2 = ((IntentFilter)o2).getPriority(); return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0); } }; /** * All filters that have been registered. */ private final HashSet<F> mFilters = new HashSet<F>(); /** * All of the * "image/*", */ private final = new

MIME types that have been registered, such as "image/jpeg", or "{@literal *}/*". HashMap<String, ArrayList<F>> mTypeToFilter HashMap<String, ArrayList<F>>();

/** * The base names of all of all fully qualified MIME types that have been * registered, such as "image" or "*". Wild card MIME types such as * "image/*" will not be here. */ private final HashMap<String, ArrayList<F>> mBaseTypeToFilter = new HashMap<String, ArrayList<F>>(); /** * The base names of all of the MIME types with a sub-type wildcard that * have been registered. For example, a filter with "image/*" will be * included here as "image" but one with "image/jpeg" will not be * included here. This also includes the "*" for the "{@literal *}/*" * MIME type. */ private final HashMap<String, ArrayList<F>> mWildTypeToFilter = new HashMap<String, ArrayList<F>>(); /** * All of the URI schemes (such as http) that have been registered. */ private final HashMap<String, ArrayList<F>> mSchemeToFilter = new HashMap<String, ArrayList<F>>();

/** * All of the actions that have been registered, but only those that did * not specify data. */ private final HashMap<String, ArrayList<F>> mActionToFilter = new HashMap<String, ArrayList<F>>(); /** * All of the actions that have been registered and specified a MIME type. */ private final HashMap<String, ArrayList<F>> mTypedActionToFilter = new HashMap<String, ArrayList<F>>(); }

ATTACHMENT J

/* * 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.android.internal.policy.PolicyManager; import com.android.internal.util.XmlUtils; import com.google.android.collect.Maps; import org.xmlpull.v1.XmlPullParserException; import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import android.content.BroadcastReceiver; android.content.ComponentName; android.content.ContentResolver; android.content.Context; android.content.ContextWrapper; android.content.IContentProvider; android.content.Intent; android.content.IntentFilter; android.content.IIntentReceiver; android.content.IntentSender; android.content.ReceiverCallNotAllowedException; android.content.ServiceConnection; android.content.SharedPreferences; android.content.pm.ActivityInfo; android.content.pm.ApplicationInfo; android.content.pm.ComponentInfo; android.content.pm.FeatureInfo; android.content.pm.IPackageDataObserver; android.content.pm.IPackageDeleteObserver; android.content.pm.IPackageInstallObserver; android.content.pm.IPackageMoveObserver; android.content.pm.IPackageManager; android.content.pm.IPackageStatsObserver; android.content.pm.InstrumentationInfo; android.content.pm.PackageInfo; android.content.pm.PackageManager; android.content.pm.PermissionGroupInfo; android.content.pm.PermissionInfo; android.content.pm.ProviderInfo; android.content.pm.ResolveInfo; android.content.pm.ServiceInfo; android.content.pm.PackageParser.Package; android.content.res.AssetManager; android.content.res.Resources; android.content.res.XmlResourceParser; android.database.sqlite.SQLiteDatabase; android.database.sqlite.SQLiteDatabase.CursorFactory; android.graphics.Bitmap; android.graphics.drawable.Drawable; android.hardware.SensorManager; android.location.ILocationManager; android.location.LocationManager; android.media.AudioManager; android.net.ConnectivityManager; android.net.IConnectivityManager; android.net.ThrottleManager; android.net.IThrottleManager; android.net.Uri; android.net.wifi.IWifiManager; android.net.wifi.WifiManager; android.os.Binder; android.os.Bundle; android.os.DropBoxManager; android.os.Environment; android.os.FileUtils; android.os.Handler; android.os.IBinder; android.os.IPowerManager;

import import import import import import import import import import import import import import import import import import import import import import

android.os.Looper; android.os.PowerManager; android.os.Process; android.os.RemoteException; android.os.ServiceManager; android.os.StatFs; android.os.Vibrator; android.os.FileUtils.FileStatus; android.os.storage.StorageManager; android.provider.Settings; android.telephony.TelephonyManager; android.text.ClipboardManager; android.util.AndroidRuntimeException; android.util.Log; android.view.ContextThemeWrapper; android.view.LayoutInflater; android.view.WindowManagerImpl; android.view.accessibility.AccessibilityManager; android.view.inputmethod.InputMethodManager; android.accounts.AccountManager; android.accounts.IAccountManager; android.app.admin.DevicePolicyManager;

import com.android.internal.os.IDropBoxManagerService; import import import import import import import import import import import import import import import import java.io.File; java.io.FileInputStream; java.io.FileNotFoundException; java.io.FileOutputStream; java.io.IOException; java.io.InputStream; java.lang.ref.WeakReference; java.util.ArrayList; java.util.HashMap; java.util.HashSet; java.util.Iterator; java.util.List; java.util.Map; java.util.Set; java.util.WeakHashMap; java.util.Map.Entry;

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 provides the base * context object for Activity and other application components. */ class ContextImpl extends Context { private final static String TAG = "ApplicationContext"; private final static boolean DEBUG = false; private final static boolean DEBUG_ICONS = false;

private private private private private private private private

static final Object sSync = new Object(); static AlarmManager sAlarmManager; static PowerManager sPowerManager; static ConnectivityManager sConnectivityManager; static ThrottleManager sThrottleManager; static WifiManager sWifiManager; static LocationManager sLocationManager; 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 WallpaperManager mWallpaperManager = null; private Context mReceiverRestrictedContext = null; private SearchManager mSearchManager = null; private SensorManager mSensorManager = null; private StorageManager mStorageManager = null; private Vibrator mVibrator = null; private LayoutInflater mLayoutInflater = null; private StatusBarManager mStatusBarManager = null; private TelephonyManager mTelephonyManager = null; private ClipboardManager mClipboardManager = null; private boolean mRestricted; private AccountManager mAccountManager; // protected by mSync private DropBoxManager mDropBoxManager = null; private DevicePolicyManager mDevicePolicyManager = null; private UiModeManager mUiModeManager = null; private final Object mSync = new Object(); private private private private private private File File File File File File mDatabasesDir; mPreferencesDir; mFilesDir; mCacheDir; mExternalFilesDir; mExternalCacheDir;

private static long sInstanceCount = 0; private static final String[] EMPTY_FILE_LIST = {}; // For debug only /* @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 (mPackageInfo != null) ? mPackageInfo.getApplication() : 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 ApplicationInfo getApplicationInfo() { if (mPackageInfo != null) { return mPackageInfo.getApplicationInfo(); } 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"); }

public File getSharedPrefsFile(String name) { return makeFilename(getPreferencesDir(), name + ".xml"); } @Override public SharedPreferences getSharedPreferences(String name, int mode) { SharedPreferencesImpl sp; File f = getSharedPrefsFile(name); 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 getExternalFilesDir(String type) { synchronized (mSync) { if (mExternalFilesDir == null) { mExternalFilesDir = Environment.getExternalStorageAppFilesDirectory( getPackageName()); } if (!mExternalFilesDir.exists()) { try { (new File(Environment.getExternalStorageAndroidDataDir(), ".nomedia")).createNewFile(); } catch (IOException e) { } if (!mExternalFilesDir.mkdirs()) { Log.w(TAG, "Unable to create external files directory"); return null; } } if (type == null) { return mExternalFilesDir; } File dir = new File(mExternalFilesDir, type); if (!dir.exists()) { if (!dir.mkdirs()) { Log.w(TAG, "Unable to create external media directory " + dir); return null; } } return dir; } } @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 getExternalCacheDir() { synchronized (mSync) { if (mExternalCacheDir == null) { mExternalCacheDir = Environment.getExternalStorageAppCacheDirectory( getPackageName()); } if (!mExternalCacheDir.exists()) { try { (new File(Environment.getExternalStorageAndroidDataDir(), ".nomedia")).createNewFile(); } catch (IOException e) { } if (!mExternalCacheDir.mkdirs()) { Log.w(TAG, "Unable to create external cache directory"); return null; } } return mExternalCacheDir; } } @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 f = validateFilePath(name, true); SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, factory); setFilePermissionsFromMode(f.getPath(), mode, 0); return db; } @Override public boolean deleteDatabase(String name) { try { File f = validateFilePath(name, false); return f.delete(); } catch (Exception e) { } return false; } @Override public File getDatabasePath(String name) { return validateFilePath(name, false); } @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() { return getWallpaperManager().getDrawable(); } @Override public Drawable peekWallpaper() { return getWallpaperManager().peekDrawable(); } @Override public int getWallpaperDesiredMinimumWidth() { return getWallpaperManager().getDesiredMinimumWidth(); } @Override public int getWallpaperDesiredMinimumHeight() { return getWallpaperManager().getDesiredMinimumHeight(); } @Override public void setWallpaper(Bitmap bitmap) throws IOException getWallpaperManager().setBitmap(bitmap); }

@Override public void setWallpaper(InputStream data) throws IOException { getWallpaperManager().setStream(data); } @Override public void clearWallpaper() throws IOException { getWallpaperManager().clear(); } @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 startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws IntentSender.SendIntentException { try { String resolvedType = null; if (fillInIntent != null) { resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver()); } int result = ActivityManagerNative.getDefault() .startActivityIntentSender(mMainThread.getApplicationThread(), intent, fillInIntent, resolvedType, null, null, 0, flagsMask, flagsValues); if (result == IActivityManager.START_CANCELED) { throw new IntentSender.SendIntentException(); } Instrumentation.checkStartActivityResult(result, null); } catch (RemoteException e) { } } @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 sendStickyOrderedBroadcast(Intent intent, 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, null, true, 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, true).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 (INPUT_METHOD_SERVICE.equals(name)) { return InputMethodManager.getInstance(this); } else if (ALARM_SERVICE.equals(name)) { return getAlarmManager(); } else if (ACCOUNT_SERVICE.equals(name)) { return getAccountManager(); } else if (POWER_SERVICE.equals(name)) { return getPowerManager(); } else if (CONNECTIVITY_SERVICE.equals(name)) { return getConnectivityManager(); } else if (THROTTLE_SERVICE.equals(name)) { return getThrottleManager(); } 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 (ACCESSIBILITY_SERVICE.equals(name)) { return AccessibilityManager.getInstance(this); } 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 (STORAGE_SERVICE.equals(name)) { return getStorageManager(); } 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 (WALLPAPER_SERVICE.equals(name)) { return getWallpaperManager(); } else if (DROPBOX_SERVICE.equals(name)) { return getDropBoxManager(); } else if (DEVICE_POLICY_SERVICE.equals(name)) { return getDevicePolicyManager(); } else if (UI_MODE_SERVICE.equals(name)) { return getUiModeManager(); } return null; } private AccountManager getAccountManager() { synchronized (mSync) { if (mAccountManager == null) { IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);

IAccountManager service = IAccountManager.Stub.asInterface(b); mAccountManager = new AccountManager(this, service); } return mAccountManager; } } 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 ThrottleManager getThrottleManager() { synchronized (sSync) { if (sThrottleManager == null) { IBinder b = ServiceManager.getService(THROTTLE_SERVICE); IThrottleManager service = IThrottleManager.Stub.asInterface(b); sThrottleManager = new ThrottleManager(service); } } return sThrottleManager; } 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 WallpaperManager getWallpaperManager() { synchronized (mSync) { if (mWallpaperManager == null) { mWallpaperManager = new WallpaperManager(getOuterContext(), mMainThread.getHandler()); } } return mWallpaperManager; } 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() { synchronized (mSync) { if (mSearchManager == null) { mSearchManager = new SearchManager(getOuterContext(), mMainThread.getHandler()); } } return mSearchManager; } private SensorManager getSensorManager() { synchronized (mSync) { if (mSensorManager == null) { mSensorManager = new SensorManager(mMainThread.getHandler().getLooper()); } } return mSensorManager; } private StorageManager getStorageManager() { synchronized (mSync) { if (mStorageManager == null) { try { mStorageManager = new StorageManager(mMainThread.getHandler().getLooper()); } catch (RemoteException rex) { Log.e(TAG, "Failed to create StorageManager", rex); mStorageManager = null; } } } return mStorageManager; } private Vibrator getVibrator() { synchronized (mSync) { if (mVibrator == null) { mVibrator = new Vibrator(); } }

return mVibrator; } private AudioManager getAudioManager() { if (mAudioManager == null) { mAudioManager = new AudioManager(this); } return mAudioManager; } private DropBoxManager getDropBoxManager() { synchronized (mSync) { if (mDropBoxManager == null) { IBinder b = ServiceManager.getService(DROPBOX_SERVICE); IDropBoxManagerService service = IDropBoxManagerService.Stub.asInterface(b); mDropBoxManager = new DropBoxManager(service); } } return mDropBoxManager; } private DevicePolicyManager getDevicePolicyManager() { synchronized (mSync) { if (mDevicePolicyManager == null) { mDevicePolicyManager = DevicePolicyManager.create(this, mMainThread.getHandler()); } } return mDevicePolicyManager; } private UiModeManager getUiModeManager() { synchronized (mSync) { if (mUiModeManager == null) { mUiModeManager = new UiModeManager(); } } return mUiModeManager; } @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 (DEBUG) { 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 ContextImpl(mMainThread.getSystemContext()); } ActivityThread.PackageInfo pi = mMainThread.getPackageInfo(packageName, flags); if (pi != null) { ContextImpl c = new ContextImpl(); c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; c.init(pi, null, mMainThread, mResources); if (c.mResources != null) { return c; } } // Should be a better exception. throw new PackageManager.NameNotFoundException( "Application package " + packageName + " not found"); } @Override public boolean isRestricted() { return mRestricted; } 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 ContextImpl createSystemContext(ActivityThread mainThread) { ContextImpl context = new ContextImpl(); context.init(Resources.getSystem(), mainThread); return context; } ContextImpl() { // For debug only //++sInstanceCount; mOuterContext = this; } /**

* Create a new ApplicationContext from an existing one. * works and operates the same as the one it is copying. * * @param context Existing application context. */ public ContextImpl(ContextImpl context) { ++sInstanceCount; mPackageInfo = context.mPackageInfo; mResources = context.mResources; mMainThread = context.mMainThread; mContentResolver = context.mContentResolver; mOuterContext = this; }

The new one

final void init(ActivityThread.PackageInfo packageInfo, IBinder activityToken, ActivityThread mainThread) { init(packageInfo, activityToken, mainThread, null); } final void init(ActivityThread.PackageInfo packageInfo, IBinder activityToken, ActivityThread mainThread, Resources container) { mPackageInfo = packageInfo; mResources = mPackageInfo.getResources(mainThread); if (mResources != null && container != null && container.getCompatibilityInfo().applicationScale != mResources.getCompatibilityInfo().applicationScale) { if (DEBUG) { Log.d(TAG, "loaded context has different scaling. Using container's" + " compatiblity info:" + container.getDisplayMetrics()); } mResources = mainThread.getTopLevelResources( mPackageInfo.getResDir(), container.getCompatibilityInfo().copy()); } 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 (DEBUG) { Log.i(TAG, "File " + name + ": mode=0x" + Integer.toHexString(mode) + ", perms=0x" + Integer.toHexString(perms)); } FileUtils.setPermissions(name, perms, -1, -1); } private File validateFilePath(String name, boolean createDirectory) { File dir; File f; if (name.charAt(0) == File.separatorChar) { String dirPath = name.substring(0, name.lastIndexOf(File.separatorChar)); dir = new File(dirPath); name = name.substring(name.lastIndexOf(File.separatorChar)); f = new File(dir, name); } else { dir = getDatabasesDir(); f = makeFilename(dir, name); } if (createDirectory && !dir.isDirectory() && dir.mkdir()) { FileUtils.setPermissions(dir.getPath(), FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, -1, -1); } return f; } 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); } @Override public String[] currentToCanonicalPackageNames(String[] names) { try { return mPM.currentToCanonicalPackageNames(names); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } } @Override public String[] canonicalToCurrentPackageNames(String[] names) { try { return mPM.canonicalToCurrentPackageNames(names); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } } @Override public Intent getLaunchIntentForPackage(String packageName) { // 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 intentToResolve = new Intent(Intent.ACTION_MAIN); intentToResolve.addCategory(Intent.CATEGORY_INFO); intentToResolve.setPackage(packageName); ResolveInfo resolveInfo = resolveActivity(intentToResolve, 0); // Otherwise, try to find a main launcher activity. if (resolveInfo == null) { // reuse the intent instance intentToResolve.removeCategory(Intent.CATEGORY_INFO); intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); intentToResolve.setPackage(packageName); resolveInfo = resolveActivity(intentToResolve, 0); } if (resolveInfo == null) { return null; } Intent intent = new Intent(Intent.ACTION_MAIN); intent.setClassName(packageName, resolveInfo.activityInfo.name); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return intent; } @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 FeatureInfo[] getSystemAvailableFeatures() { try { return mPM.getSystemAvailableFeatures(); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } } @Override public boolean hasSystemFeature(String name) { try { return mPM.hasSystemFeature(name); } 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 boolean addPermissionAsync(PermissionInfo info) { try { return mPM.addPermissionAsync(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 int checkSignatures(int uid1, int uid2) { try { return mPM.checkUidSignatures(uid1, uid2); } 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 (false) { RuntimeException e = new RuntimeException("here"); e.fillInStackTrace(); Log.w(TAG, "Getting drawable 0x" + Integer.toHexString(resid) + " from package " + packageName + ": app scale=" + r.getCompatibilityInfo().applicationScale + ", caller scale=" + mContext.getResources().getCompatibilityInfo().applicationScale, e); } 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) { return info.loadIcon(this); } @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, mContext.mPackageInfo); 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(ContextImpl 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 putCachedIcon(ResourceName name, Drawable dr) { synchronized (sSync) { sIconCache.put(name, new WeakReference<Drawable>(dr)); if (DEBUG_ICONS) Log.v(TAG, "Added cached drawable for " + name + ": " + dr); } } static final void handlePackageBroadcast(int cmd, String[] pkgList, boolean hasPkgInfo) { boolean immediateGc = false; if (cmd == IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE) { immediateGc = true; } if (pkgList != null && (pkgList.length > 0)) { boolean needCleanup = false; for (String ssp : pkgList) { synchronized (sSync) { if (sIconCache.size() > 0) { 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; } } } if (sStringCache.size() > 0) { Iterator<ResourceName> 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 || hasPkgInfo) { if (immediateGc) { // Schedule an immediate gc. Runtime.getRuntime().gc(); } else { 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) { synchronized (sSync) { sStringCache.put(name, new WeakReference<CharSequence>(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) { return info.loadLabel(this); } @Override public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName) { try { mPM.installPackage(packageURI, observer, flags, installerPackageName); } catch (RemoteException e) { // Should never happen! } } @Override public void movePackage(String packageName, IPackageMoveObserver observer, int flags) { try { mPM.movePackage(packageName, observer, flags); } catch (RemoteException e) { // Should never happen! } } @Override public String getInstallerPackageName(String packageName) { try { return mPM.getInstallerPackageName(packageName); } catch (RemoteException e) { // Should never happen! } return null; }

@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 freeStorageSize, IntentSender pi) { try { mPM.freeStorage(freeStorageSize, pi); } 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 replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { try { mPM.replacePreferredActivity(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 ContextImpl mContext; private final IPackageManager mPM;

private static final Object sSync = new Object(); 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 private private private private private final File mFile; final File mBackupFile; final int mMode; Map mMap; final FileStatus mFileStatus = new FileStatus(); long mTimestamp;

private static final Object mContent = new Object(); private WeakHashMap<OnSharedPreferenceChangeListener, Object> 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 WeakHashMap<OnSharedPreferenceChangeListener, Object>(); } 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) { mListeners.put(listener, mContent); } } 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;

Set<OnSharedPreferenceChangeListener> listeners = null; synchronized (SharedPreferencesImpl.this) { hasListeners = mListeners.size() > 0; if (hasListeners) { keysModified = new ArrayList<String>(); listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet()); } synchronized (this) { if (mClear) { mMap.clear(); mClear = false; } for (Entry<String, Object> e : mModified.entrySet()) { 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); for (OnSharedPreferenceChangeListener listener : listeners) { if (listener != null) { listener.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 (!mBackupFile.exists()) {

if (!mFile.renameTo(mBackupFile)) { Log.e(TAG, "Couldn't rename file " + mFile + " to backup file " + mBackupFile); return false; } } else { mFile.delete(); } } // 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 there is one. mBackupFile.delete(); 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; } } }

You might also like