Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial checkin of google android gingerbread code

Change-Id: Ie177bfbe37a187065b293d4d123acf4939b8d725
  • Loading branch information...
commit 940e8cd3789006a1aa4e3a36c8f852f7b1c47d87 1 parent 79ea40d
@MiuiAnderson MiuiAnderson authored
Showing with 71,059 additions and 0 deletions.
  1. +1,078 −0 src/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java
  2. +3,695 −0 src/frameworks/base/core/java/android/app/ActivityThread.java
  3. +3,220 −0 src/frameworks/base/core/java/android/app/ContextImpl.java
  4. +1,135 −0 src/frameworks/base/core/java/android/app/DownloadManager.java
  5. +549 −0 src/frameworks/base/core/java/android/app/Notification.java
  6. +288 −0 src/frameworks/base/core/java/android/content/pm/PackageItemInfo.java
  7. +3,263 −0 src/frameworks/base/core/java/android/content/pm/PackageParser.java
  8. +323 −0 src/frameworks/base/core/java/android/content/pm/ResolveInfo.java
  9. +768 −0 src/frameworks/base/core/java/android/content/res/AssetManager.java
  10. +664 −0 src/frameworks/base/core/java/android/content/res/Configuration.java
  11. +1,979 −0 src/frameworks/base/core/java/android/content/res/Resources.java
  12. +296 −0 src/frameworks/base/core/java/android/preference/PreferenceActivity.java
  13. +260 −0 src/frameworks/base/core/java/android/preference/PreferenceScreen.java
  14. +3,755 −0 src/frameworks/base/core/java/android/provider/Settings.java
  15. +1,961 −0 src/frameworks/base/core/java/android/text/Layout.java
  16. +1,321 −0 src/frameworks/base/core/java/android/text/StaticLayout.java
  17. +68 −0 src/frameworks/base/core/java/android/view/HapticFeedbackConstants.java
  18. +906 −0 src/frameworks/base/core/java/com/android/internal/app/AlertController.java
  19. +381 −0 src/frameworks/base/core/java/com/android/internal/app/ShutdownThread.java
  20. +100 −0 src/frameworks/base/core/java/com/android/internal/view/menu/ExpandedMenuView.java
  21. +539 −0 src/frameworks/base/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
  22. +241 −0 src/frameworks/base/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
  23. +1,136 −0 src/frameworks/base/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
  24. +792 −0 src/frameworks/base/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
  25. +62 −0 src/frameworks/base/policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
  26. +2,810 −0 src/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
  27. +2,414 −0 src/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
  28. +69 −0 src/frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java
  29. +10,293 −0 src/frameworks/base/services/java/com/android/server/PackageManagerService.java
  30. +10,652 −0 src/frameworks/base/services/java/com/android/server/WindowManagerService.java
  31. +12,484 −0 src/frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
  32. +3,557 −0 src/frameworks/base/services/java/com/android/server/am/ActivityStack.java
  33. BIN  system/framework/android.policy.jar
  34. BIN  system/framework/framework.jar
  35. BIN  system/framework/services.jar
View
1,078 src/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -0,0 +1,1078 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.pm;
+
+import com.android.internal.content.PackageHelper;
+
+import android.app.ActivityManagerNative;
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageInstallObserver;
+import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.WeakHashMap;
+
+public final class Pm {
+ IPackageManager mPm;
+
+ private WeakHashMap<String, Resources> mResourceCache
+ = new WeakHashMap<String, Resources>();
+
+ private String[] mArgs;
+ private int mNextArg;
+ private String mCurArgData;
+
+ private static final String PM_NOT_RUNNING_ERR =
+ "Error: Could not access the Package Manager. Is the system running?";
+
+ public static void main(String[] args) {
+ new Pm().run(args);
+ }
+
+ public void run(String[] args) {
+ boolean validCommand = false;
+ if (args.length < 1) {
+ showUsage();
+ return;
+ }
+
+ mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ if (mPm == null) {
+ System.err.println(PM_NOT_RUNNING_ERR);
+ return;
+ }
+
+ mArgs = args;
+ String op = args[0];
+ mNextArg = 1;
+
+ if ("list".equals(op)) {
+ runList();
+ return;
+ }
+
+ if ("path".equals(op)) {
+ runPath();
+ return;
+ }
+
+ if ("install".equals(op)) {
+ runInstall();
+ return;
+ }
+
+ if ("uninstall".equals(op)) {
+ runUninstall();
+ return;
+ }
+
+ if ("clear".equals(op)) {
+ runClear();
+ return;
+ }
+
+ if ("enable".equals(op)) {
+ runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+ return;
+ }
+
+ if ("disable".equals(op)) {
+ runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+ return;
+ }
+
+ if ("setInstallLocation".equals(op)) {
+ runSetInstallLocation();
+ return;
+ }
+
+ if ("getInstallLocation".equals(op)) {
+ runGetInstallLocation();
+ return;
+ }
+
+ try {
+ if (args.length == 1) {
+ if (args[0].equalsIgnoreCase("-l")) {
+ validCommand = true;
+ runListPackages(false);
+ } else if (args[0].equalsIgnoreCase("-lf")){
+ validCommand = true;
+ runListPackages(true);
+ }
+ } else if (args.length == 2) {
+ if (args[0].equalsIgnoreCase("-p")) {
+ validCommand = true;
+ displayPackageFilePath(args[1]);
+ }
+ }
+ } finally {
+ if (validCommand == false) {
+ if (op != null) {
+ System.err.println("Error: unknown command '" + op + "'");
+ }
+ showUsage();
+ }
+ }
+ }
+
+ /**
+ * Execute the list sub-command.
+ *
+ * pm list [package | packages]
+ * pm list permission-groups
+ * pm list permissions
+ * pm list features
+ * pm list libraries
+ * pm list instrumentation
+ */
+ private void runList() {
+ String type = nextArg();
+ if (type == null) {
+ System.err.println("Error: didn't specify type of data to list");
+ showUsage();
+ return;
+ }
+ if ("package".equals(type) || "packages".equals(type)) {
+ runListPackages(false);
+ } else if ("permission-groups".equals(type)) {
+ runListPermissionGroups();
+ } else if ("permissions".equals(type)) {
+ runListPermissions();
+ } else if ("features".equals(type)) {
+ runListFeatures();
+ } else if ("libraries".equals(type)) {
+ runListLibraries();
+ } else if ("instrumentation".equals(type)) {
+ runListInstrumentation();
+ } else {
+ System.err.println("Error: unknown list type '" + type + "'");
+ showUsage();
+ }
+ }
+
+ /**
+ * Lists all the installed packages.
+ */
+ private void runListPackages(boolean showApplicationPackage) {
+ int getFlags = 0;
+ boolean listDisabled = false, listEnabled = false;
+ try {
+ String opt;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("-l")) {
+ // old compat
+ } else if (opt.equals("-lf")) {
+ showApplicationPackage = true;
+ } else if (opt.equals("-f")) {
+ showApplicationPackage = true;
+ } else if (opt.equals("-d")) {
+ listDisabled = true;
+ } else if (opt.equals("-e")) {
+ listEnabled = true;
+ } else if (opt.equals("-u")) {
+ getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ showUsage();
+ return;
+ }
+ }
+ } catch (RuntimeException ex) {
+ System.err.println("Error: " + ex.toString());
+ showUsage();
+ return;
+ }
+
+ String filter = nextArg();
+
+ try {
+ final List<PackageInfo> packages = getInstalledPackages(mPm, getFlags);
+
+ int count = packages.size();
+ for (int p = 0 ; p < count ; p++) {
+ PackageInfo info = packages.get(p);
+ if (filter != null && !info.packageName.contains(filter)) {
+ continue;
+ }
+ if ((!listDisabled || !info.applicationInfo.enabled) &&
+ (!listEnabled || info.applicationInfo.enabled)) {
+ System.out.print("package:");
+ if (showApplicationPackage) {
+ System.out.print(info.applicationInfo.sourceDir);
+ System.out.print("=");
+ }
+ System.out.println(info.packageName);
+ }
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags)
+ throws RemoteException {
+ final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>();
+ PackageInfo lastItem = null;
+ ParceledListSlice<PackageInfo> slice;
+
+ do {
+ final String lastKey = lastItem != null ? lastItem.packageName : null;
+ slice = pm.getInstalledPackages(flags, lastKey);
+ lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
+ } while (!slice.isLastSlice());
+
+ return packageInfos;
+ }
+
+ /**
+ * Lists all of the features supported by the current device.
+ *
+ * pm list features
+ */
+ private void runListFeatures() {
+ try {
+ List<FeatureInfo> list = new ArrayList<FeatureInfo>();
+ FeatureInfo[] rawList = mPm.getSystemAvailableFeatures();
+ for (int i=0; i<rawList.length; i++) {
+ list.add(rawList[i]);
+ }
+
+
+ // Sort by name
+ Collections.sort(list, new Comparator<FeatureInfo>() {
+ public int compare(FeatureInfo o1, FeatureInfo o2) {
+ if (o1.name == o2.name) return 0;
+ if (o1.name == null) return -1;
+ if (o2.name == null) return 1;
+ return o1.name.compareTo(o2.name);
+ }
+ });
+
+ int count = (list != null) ? list.size() : 0;
+ for (int p = 0; p < count; p++) {
+ FeatureInfo fi = list.get(p);
+ System.out.print("feature:");
+ if (fi.name != null) System.out.println(fi.name);
+ else System.out.println("reqGlEsVersion=0x"
+ + Integer.toHexString(fi.reqGlEsVersion));
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ /**
+ * Lists all of the libraries supported by the current device.
+ *
+ * pm list libraries
+ */
+ private void runListLibraries() {
+ try {
+ List<String> list = new ArrayList<String>();
+ String[] rawList = mPm.getSystemSharedLibraryNames();
+ for (int i=0; i<rawList.length; i++) {
+ list.add(rawList[i]);
+ }
+
+
+ // Sort by name
+ Collections.sort(list, new Comparator<String>() {
+ public int compare(String o1, String o2) {
+ if (o1 == o2) return 0;
+ if (o1 == null) return -1;
+ if (o2 == null) return 1;
+ return o1.compareTo(o2);
+ }
+ });
+
+ int count = (list != null) ? list.size() : 0;
+ for (int p = 0; p < count; p++) {
+ String lib = list.get(p);
+ System.out.print("library:");
+ System.out.println(lib);
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ /**
+ * Lists all of the installed instrumentation, or all for a given package
+ *
+ * pm list instrumentation [package] [-f]
+ */
+ private void runListInstrumentation() {
+ int flags = 0; // flags != 0 is only used to request meta-data
+ boolean showPackage = false;
+ String targetPackage = null;
+
+ try {
+ String opt;
+ while ((opt=nextArg()) != null) {
+ if (opt.equals("-f")) {
+ showPackage = true;
+ } else if (opt.charAt(0) != '-') {
+ targetPackage = opt;
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ showUsage();
+ return;
+ }
+ }
+ } catch (RuntimeException ex) {
+ System.err.println("Error: " + ex.toString());
+ showUsage();
+ return;
+ }
+
+ try {
+ List<InstrumentationInfo> list = mPm.queryInstrumentation(targetPackage, flags);
+
+ // Sort by target package
+ Collections.sort(list, new Comparator<InstrumentationInfo>() {
+ public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
+ return o1.targetPackage.compareTo(o2.targetPackage);
+ }
+ });
+
+ int count = (list != null) ? list.size() : 0;
+ for (int p = 0; p < count; p++) {
+ InstrumentationInfo ii = list.get(p);
+ System.out.print("instrumentation:");
+ if (showPackage) {
+ System.out.print(ii.sourceDir);
+ System.out.print("=");
+ }
+ ComponentName cn = new ComponentName(ii.packageName, ii.name);
+ System.out.print(cn.flattenToShortString());
+ System.out.print(" (target=");
+ System.out.print(ii.targetPackage);
+ System.out.println(")");
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ /**
+ * Lists all the known permission groups.
+ */
+ private void runListPermissionGroups() {
+ try {
+ List<PermissionGroupInfo> pgs = mPm.getAllPermissionGroups(0);
+
+ int count = pgs.size();
+ for (int p = 0 ; p < count ; p++) {
+ PermissionGroupInfo pgi = pgs.get(p);
+ System.out.print("permission group:");
+ System.out.println(pgi.name);
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized) {
+ if (nonLocalized != null) {
+ return nonLocalized.toString();
+ }
+ Resources r = getResources(pii);
+ if (r != null) {
+ return r.getString(res);
+ }
+ return null;
+ }
+
+ /**
+ * Lists all the permissions in a group.
+ */
+ private void runListPermissions() {
+ try {
+ boolean labels = false;
+ boolean groups = false;
+ boolean userOnly = false;
+ boolean summary = false;
+ boolean dangerousOnly = false;
+ String opt;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("-f")) {
+ labels = true;
+ } else if (opt.equals("-g")) {
+ groups = true;
+ } else if (opt.equals("-s")) {
+ groups = true;
+ labels = true;
+ summary = true;
+ } else if (opt.equals("-u")) {
+ userOnly = true;
+ } else if (opt.equals("-d")) {
+ dangerousOnly = true;
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ showUsage();
+ return;
+ }
+ }
+
+ String grp = nextOption();
+ ArrayList<String> groupList = new ArrayList<String>();
+ if (groups) {
+ List<PermissionGroupInfo> infos =
+ mPm.getAllPermissionGroups(0);
+ for (int i=0; i<infos.size(); i++) {
+ groupList.add(infos.get(i).name);
+ }
+ groupList.add(null);
+ } else {
+ groupList.add(grp);
+ }
+
+ if (dangerousOnly) {
+ System.out.println("Dangerous Permissions:");
+ System.out.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ PermissionInfo.PROTECTION_DANGEROUS);
+ if (userOnly) {
+ System.out.println("Normal Permissions:");
+ System.out.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ PermissionInfo.PROTECTION_NORMAL,
+ PermissionInfo.PROTECTION_NORMAL);
+ }
+ } else if (userOnly) {
+ System.out.println("Dangerous and Normal Permissions:");
+ System.out.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ PermissionInfo.PROTECTION_NORMAL,
+ PermissionInfo.PROTECTION_DANGEROUS);
+ } else {
+ System.out.println("All Permissions:");
+ System.out.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ -10000, 10000);
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ private void doListPermissions(ArrayList<String> groupList,
+ boolean groups, boolean labels, boolean summary,
+ int startProtectionLevel, int endProtectionLevel)
+ throws RemoteException {
+ for (int i=0; i<groupList.size(); i++) {
+ String groupName = groupList.get(i);
+ String prefix = "";
+ if (groups) {
+ if (i > 0) System.out.println("");
+ if (groupName != null) {
+ PermissionGroupInfo pgi = mPm.getPermissionGroupInfo(
+ groupName, 0);
+ if (summary) {
+ Resources res = getResources(pgi);
+ if (res != null) {
+ System.out.print(loadText(pgi, pgi.labelRes,
+ pgi.nonLocalizedLabel) + ": ");
+ } else {
+ System.out.print(pgi.name + ": ");
+
+ }
+ } else {
+ System.out.println((labels ? "+ " : "")
+ + "group:" + pgi.name);
+ if (labels) {
+ System.out.println(" package:" + pgi.packageName);
+ Resources res = getResources(pgi);
+ if (res != null) {
+ System.out.println(" label:"
+ + loadText(pgi, pgi.labelRes,
+ pgi.nonLocalizedLabel));
+ System.out.println(" description:"
+ + loadText(pgi, pgi.descriptionRes,
+ pgi.nonLocalizedDescription));
+ }
+ }
+ }
+ } else {
+ System.out.println(((labels && !summary)
+ ? "+ " : "") + "ungrouped:");
+ }
+ prefix = " ";
+ }
+ List<PermissionInfo> ps = mPm.queryPermissionsByGroup(
+ groupList.get(i), 0);
+ int count = ps.size();
+ boolean first = true;
+ for (int p = 0 ; p < count ; p++) {
+ PermissionInfo pi = ps.get(p);
+ if (groups && groupName == null && pi.group != null) {
+ continue;
+ }
+ if (pi.protectionLevel < startProtectionLevel
+ || pi.protectionLevel > endProtectionLevel) {
+ continue;
+ }
+ if (summary) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ Resources res = getResources(pi);
+ if (res != null) {
+ System.out.print(loadText(pi, pi.labelRes,
+ pi.nonLocalizedLabel));
+ } else {
+ System.out.print(pi.name);
+ }
+ } else {
+ System.out.println(prefix + (labels ? "+ " : "")
+ + "permission:" + pi.name);
+ if (labels) {
+ System.out.println(prefix + " package:" + pi.packageName);
+ Resources res = getResources(pi);
+ if (res != null) {
+ System.out.println(prefix + " label:"
+ + loadText(pi, pi.labelRes,
+ pi.nonLocalizedLabel));
+ System.out.println(prefix + " description:"
+ + loadText(pi, pi.descriptionRes,
+ pi.nonLocalizedDescription));
+ }
+ String protLevel = "unknown";
+ switch(pi.protectionLevel) {
+ case PermissionInfo.PROTECTION_DANGEROUS:
+ protLevel = "dangerous";
+ break;
+ case PermissionInfo.PROTECTION_NORMAL:
+ protLevel = "normal";
+ break;
+ case PermissionInfo.PROTECTION_SIGNATURE:
+ protLevel = "signature";
+ break;
+ case PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM:
+ protLevel = "signatureOrSystem";
+ break;
+ }
+ System.out.println(prefix + " protectionLevel:" + protLevel);
+ }
+ }
+ }
+
+ if (summary) {
+ System.out.println("");
+ }
+ }
+ }
+
+ private void runPath() {
+ String pkg = nextArg();
+ if (pkg == null) {
+ System.err.println("Error: no package specified");
+ showUsage();
+ return;
+ }
+ displayPackageFilePath(pkg);
+ }
+
+ class PackageInstallObserver extends IPackageInstallObserver.Stub {
+ boolean finished;
+ int result;
+
+ public void packageInstalled(String name, int status) {
+ synchronized( this) {
+ finished = true;
+ result = status;
+ notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Converts a failure code into a string by using reflection to find a matching constant
+ * in PackageManager.
+ */
+ private String installFailureToString(int result) {
+ Field[] fields = PackageManager.class.getFields();
+ for (Field f: fields) {
+ if (f.getType() == int.class) {
+ int modifiers = f.getModifiers();
+ // only look at public final static fields.
+ if (((modifiers & Modifier.FINAL) != 0) &&
+ ((modifiers & Modifier.PUBLIC) != 0) &&
+ ((modifiers & Modifier.STATIC) != 0)) {
+ String fieldName = f.getName();
+ if (fieldName.startsWith("INSTALL_FAILED_") ||
+ fieldName.startsWith("INSTALL_PARSE_FAILED_")) {
+ // get the int value and compare it to result.
+ try {
+ if (result == f.getInt(null)) {
+ return fieldName;
+ }
+ } catch (IllegalAccessException e) {
+ // this shouldn't happen since we only look for public static fields.
+ }
+ }
+ }
+ }
+ }
+
+ // couldn't find a matching constant? return the value
+ return Integer.toString(result);
+ }
+
+ private void runSetInstallLocation() {
+ int loc;
+
+ String arg = nextArg();
+ if (arg == null) {
+ System.err.println("Error: no install location specified.");
+ showUsage();
+ return;
+ }
+ try {
+ loc = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ System.err.println("Error: install location has to be a number.");
+ showUsage();
+ return;
+ }
+ try {
+ if (!mPm.setInstallLocation(loc)) {
+ System.err.println("Error: install location has to be a number.");
+ showUsage();
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ private void runGetInstallLocation() {
+ try {
+ int loc = mPm.getInstallLocation();
+ String locStr = "invalid";
+ if (loc == PackageHelper.APP_INSTALL_AUTO) {
+ locStr = "auto";
+ } else if (loc == PackageHelper.APP_INSTALL_INTERNAL) {
+ locStr = "internal";
+ } else if (loc == PackageHelper.APP_INSTALL_EXTERNAL) {
+ locStr = "external";
+ }
+ System.out.println(loc + "[" + locStr + "]");
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ private void runInstall() {
+ int installFlags = 0;
+ String installerPackageName = null;
+
+ String opt;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("-l")) {
+ installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+ } else if (opt.equals("-r")) {
+ installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ } else if (opt.equals("-i")) {
+ installerPackageName = nextOptionData();
+ if (installerPackageName == null) {
+ System.err.println("Error: no value specified for -i");
+ showUsage();
+ return;
+ }
+ } else if (opt.equals("-t")) {
+ installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+ } else if (opt.equals("-s")) {
+ // Override if -s option is specified.
+ installFlags |= PackageManager.INSTALL_EXTERNAL;
+ } else if (opt.equals("-f")) {
+ // Override if -s option is specified.
+ installFlags |= PackageManager.INSTALL_INTERNAL;
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ showUsage();
+ return;
+ }
+ }
+
+ String apkFilePath = nextArg();
+ System.err.println("\tpkg: " + apkFilePath);
+ if (apkFilePath == null) {
+ System.err.println("Error: no package specified");
+ showUsage();
+ return;
+ }
+
+ PackageInstallObserver obs = new PackageInstallObserver();
+ try {
+ mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,
+ installerPackageName);
+
+ synchronized (obs) {
+ while (!obs.finished) {
+ try {
+ obs.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
+ System.out.println("Success");
+ } else {
+ System.err.println("Failure ["
+ + installFailureToString(obs.result)
+ + "]");
+ }
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
+ boolean finished;
+ boolean result;
+
+ public void packageDeleted(boolean succeeded) {
+ synchronized (this) {
+ finished = true;
+ result = succeeded;
+ notifyAll();
+ }
+ }
+ }
+
+ private void runUninstall() {
+ int unInstallFlags = 0;
+
+ String opt = nextOption();
+ if (opt != null && opt.equals("-k")) {
+ unInstallFlags = PackageManager.DONT_DELETE_DATA;
+ }
+
+ String pkg = nextArg();
+ if (pkg == null) {
+ System.err.println("Error: no package specified");
+ showUsage();
+ return;
+ }
+ boolean result = deletePackage(pkg, unInstallFlags);
+ if (result) {
+ System.out.println("Success");
+ } else {
+ System.out.println("Failure");
+ }
+ }
+
+ private boolean deletePackage(String pkg, int unInstallFlags) {
+ PackageDeleteObserver obs = new PackageDeleteObserver();
+ try {
+ mPm.deletePackage(pkg, obs, unInstallFlags);
+
+ synchronized (obs) {
+ while (!obs.finished) {
+ try {
+ obs.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ return obs.result;
+ }
+
+ class ClearDataObserver extends IPackageDataObserver.Stub {
+ boolean finished;
+ boolean result;
+
+ @Override
+ public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException {
+ synchronized (this) {
+ finished = true;
+ result = succeeded;
+ notifyAll();
+ }
+ }
+
+ }
+
+ private void runClear() {
+ String pkg = nextArg();
+ if (pkg == null) {
+ System.err.println("Error: no package specified");
+ showUsage();
+ return;
+ }
+
+ ClearDataObserver obs = new ClearDataObserver();
+ try {
+ if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs)) {
+ System.err.println("Failed");
+ }
+
+ synchronized (obs) {
+ while (!obs.finished) {
+ try {
+ obs.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ if (obs.result) {
+ System.err.println("Success");
+ } else {
+ System.err.println("Failed");
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ private static String enabledSettingToString(int state) {
+ switch (state) {
+ case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+ return "default";
+ case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+ return "enabled";
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+ return "disabled";
+ }
+ return "unknown";
+ }
+
+ private void runSetEnabledSetting(int state) {
+ String pkg = nextArg();
+ if (pkg == null) {
+ System.err.println("Error: no package or component specified");
+ showUsage();
+ return;
+ }
+ ComponentName cn = ComponentName.unflattenFromString(pkg);
+ if (cn == null) {
+ try {
+ mPm.setApplicationEnabledSetting(pkg, state, 0);
+ System.err.println("Package " + pkg + " new state: "
+ + enabledSettingToString(
+ mPm.getApplicationEnabledSetting(pkg)));
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ } else {
+ try {
+ mPm.setComponentEnabledSetting(cn, state, 0);
+ System.err.println("Component " + cn.toShortString() + " new state: "
+ + enabledSettingToString(
+ mPm.getComponentEnabledSetting(cn)));
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+ }
+
+ /**
+ * Displays the package file for a package.
+ * @param pckg
+ */
+ private void displayPackageFilePath(String pckg) {
+ try {
+ PackageInfo info = mPm.getPackageInfo(pckg, 0);
+ if (info != null && info.applicationInfo != null) {
+ System.out.print("package:");
+ System.out.println(info.applicationInfo.sourceDir);
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
+ private Resources getResources(PackageItemInfo pii) {
+ Resources res = mResourceCache.get(pii.packageName);
+ if (res != null) return res;
+
+ try {
+ ApplicationInfo ai = mPm.getApplicationInfo(pii.packageName, 0);
+ AssetManager am = new AssetManager();
+ am.addAssetPath(ai.publicSourceDir);
+ res = new Resources(am, null, null);
+ mResourceCache.put(pii.packageName, res);
+ return res;
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ return null;
+ }
+ }
+
+ private String nextOption() {
+ if (mNextArg >= mArgs.length) {
+ return null;
+ }
+ String arg = mArgs[mNextArg];
+ if (!arg.startsWith("-")) {
+ return null;
+ }
+ mNextArg++;
+ if (arg.equals("--")) {
+ return null;
+ }
+ if (arg.length() > 1 && arg.charAt(1) != '-') {
+ if (arg.length() > 2) {
+ mCurArgData = arg.substring(2);
+ return arg.substring(0, 2);
+ } else {
+ mCurArgData = null;
+ return arg;
+ }
+ }
+ mCurArgData = null;
+ return arg;
+ }
+
+ private String nextOptionData() {
+ if (mCurArgData != null) {
+ return mCurArgData;
+ }
+ if (mNextArg >= mArgs.length) {
+ return null;
+ }
+ String data = mArgs[mNextArg];
+ mNextArg++;
+ return data;
+ }
+
+ private String nextArg() {
+ if (mNextArg >= mArgs.length) {
+ return null;
+ }
+ String arg = mArgs[mNextArg];
+ mNextArg++;
+ return arg;
+ }
+
+ private static void showUsage() {
+ System.err.println("usage: pm [list|path|install|uninstall]");
+ System.err.println(" pm list packages [-f] [-d] [-e] [-u] [FILTER]");
+ System.err.println(" pm list permission-groups");
+ System.err.println(" pm list permissions [-g] [-f] [-d] [-u] [GROUP]");
+ System.err.println(" pm list instrumentation [-f] [TARGET-PACKAGE]");
+ System.err.println(" pm list features");
+ System.err.println(" pm list libraries");
+ System.err.println(" pm path PACKAGE");
+ System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH");
+ System.err.println(" pm uninstall [-k] PACKAGE");
+ System.err.println(" pm clear PACKAGE");
+ System.err.println(" pm enable PACKAGE_OR_COMPONENT");
+ System.err.println(" pm disable PACKAGE_OR_COMPONENT");
+ System.err.println(" pm setInstallLocation [0/auto] [1/internal] [2/external]");
+ System.err.println("");
+ System.err.println("The list packages command prints all packages, optionally only");
+ System.err.println("those whose package name contains the text in FILTER. Options:");
+ System.err.println(" -f: see their associated file.");
+ System.err.println(" -d: filter to include disbled packages.");
+ System.err.println(" -e: filter to include enabled packages.");
+ System.err.println(" -u: also include uninstalled packages.");
+ System.err.println("");
+ System.err.println("The list permission-groups command prints all known");
+ System.err.println("permission groups.");
+ System.err.println("");
+ System.err.println("The list permissions command prints all known");
+ System.err.println("permissions, optionally only those in GROUP. Options:");
+ System.err.println(" -g: organize by group.");
+ System.err.println(" -f: print all information.");
+ System.err.println(" -s: short summary.");
+ System.err.println(" -d: only list dangerous permissions.");
+ System.err.println(" -u: list only the permissions users will see.");
+ System.err.println("");
+ System.err.println("The list instrumentation command prints all instrumentations,");
+ System.err.println("or only those that target a specified package. Options:");
+ System.err.println(" -f: see their associated file.");
+ System.err.println("");
+ System.err.println("The list features command prints all features of the system.");
+ System.err.println("");
+ System.err.println("The path command prints the path to the .apk of a package.");
+ System.err.println("");
+ System.err.println("The install command installs a package to the system. Options:");
+ System.err.println(" -l: install the package with FORWARD_LOCK.");
+ System.err.println(" -r: reinstall an exisiting app, keeping its data.");
+ System.err.println(" -t: allow test .apks to be installed.");
+ System.err.println(" -i: specify the installer package name.");
+ System.err.println(" -s: install package on sdcard.");
+ System.err.println(" -f: install package on internal flash.");
+ System.err.println("");
+ System.err.println("The uninstall command removes a package from the system. Options:");
+ System.err.println(" -k: keep the data and cache directories around.");
+ System.err.println("after the package removal.");
+ System.err.println("");
+ System.err.println("The clear command deletes all data associated with a package.");
+ System.err.println("");
+ System.err.println("The enable and disable commands change the enabled state of");
+ System.err.println("a given package or component (written as \"package/class\").");
+ System.err.println("");
+ System.err.println("The getInstallLocation command gets the current install location");
+ System.err.println(" 0 [auto]: Let system decide the best location");
+ System.err.println(" 1 [internal]: Install on internal device storage");
+ System.err.println(" 2 [external]: Install on external media");
+ System.err.println("");
+ System.err.println("The setInstallLocation command changes the default install location");
+ System.err.println(" 0 [auto]: Let system decide the best location");
+ System.err.println(" 1 [internal]: Install on internal device storage");
+ System.err.println(" 2 [external]: Install on external media");
+ }
+}
View
3,695 src/frameworks/base/core/java/android/app/ActivityThread.java
3,695 additions, 0 deletions not shown
View
3,220 src/frameworks/base/core/java/android/app/ContextImpl.java
3,220 additions, 0 deletions not shown
View
1,135 src/frameworks/base/core/java/android/app/DownloadManager.java
@@ -0,0 +1,1135 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.provider.BaseColumns;
+import android.provider.Downloads;
+import android.util.Pair;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The download manager is a system service that handles long-running HTTP downloads. Clients may
+ * request that a URI be downloaded to a particular destination file. The download manager will
+ * conduct the download in the background, taking care of HTTP interactions and retrying downloads
+ * after failures or across connectivity changes and system reboots.
+ *
+ * Instances of this class should be obtained through
+ * {@link android.content.Context#getSystemService(String)} by passing
+ * {@link android.content.Context#DOWNLOAD_SERVICE}.
+ *
+ * Apps that request downloads through this API should register a broadcast receiver for
+ * {@link #ACTION_NOTIFICATION_CLICKED} to appropriately handle when the user clicks on a running
+ * download in a notification or from the downloads UI.
+ */
+public class DownloadManager {
+ private static final String TAG = "DownloadManager";
+
+ /**
+ * An identifier for a particular download, unique across the system. Clients use this ID to
+ * make subsequent calls related to the download.
+ */
+ public final static String COLUMN_ID = BaseColumns._ID;
+
+ /**
+ * The client-supplied title for this download. This will be displayed in system notifications.
+ * Defaults to the empty string.
+ */
+ public final static String COLUMN_TITLE = "title";
+
+ /**
+ * The client-supplied description of this download. This will be displayed in system
+ * notifications. Defaults to the empty string.
+ */
+ public final static String COLUMN_DESCRIPTION = "description";
+
+ /**
+ * URI to be downloaded.
+ */
+ public final static String COLUMN_URI = "uri";
+
+ /**
+ * Internet Media Type of the downloaded file. If no value is provided upon creation, this will
+ * initially be null and will be filled in based on the server's response once the download has
+ * started.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a>
+ */
+ public final static String COLUMN_MEDIA_TYPE = "media_type";
+
+ /**
+ * Total size of the download in bytes. This will initially be -1 and will be filled in once
+ * the download starts.
+ */
+ public final static String COLUMN_TOTAL_SIZE_BYTES = "total_size";
+
+ /**
+ * Uri where downloaded file will be stored. If a destination is supplied by client, that URI
+ * will be used here. Otherwise, the value will initially be null and will be filled in with a
+ * generated URI once the download has started.
+ */
+ public final static String COLUMN_LOCAL_URI = "local_uri";
+
+ /**
+ * Current status of the download, as one of the STATUS_* constants.
+ */
+ public final static String COLUMN_STATUS = "status";
+
+ /**
+ * Provides more detail on the status of the download. Its meaning depends on the value of
+ * {@link #COLUMN_STATUS}.
+ *
+ * When {@link #COLUMN_STATUS} is {@link #STATUS_FAILED}, this indicates the type of error that
+ * occurred. If an HTTP error occurred, this will hold the HTTP status code as defined in RFC
+ * 2616. Otherwise, it will hold one of the ERROR_* constants.
+ *
+ * When {@link #COLUMN_STATUS} is {@link #STATUS_PAUSED}, this indicates why the download is
+ * paused. It will hold one of the PAUSED_* constants.
+ *
+ * If {@link #COLUMN_STATUS} is neither {@link #STATUS_FAILED} nor {@link #STATUS_PAUSED}, this
+ * column's value is undefined.
+ *
+ * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1">RFC 2616
+ * status codes</a>
+ */
+ public final static String COLUMN_REASON = "reason";
+
+ /**
+ * Number of bytes download so far.
+ */
+ public final static String COLUMN_BYTES_DOWNLOADED_SO_FAR = "bytes_so_far";
+
+ /**
+ * Timestamp when the download was last modified, in {@link System#currentTimeMillis
+ * System.currentTimeMillis()} (wall clock time in UTC).
+ */
+ public final static String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
+
+ /**
+ * The URI to the corresponding entry in MediaProvider for this downloaded entry. It is
+ * used to delete the entries from MediaProvider database when it is deleted from the
+ * downloaded list.
+ */
+ public static final String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
+
+ /**
+ * Value of {@link #COLUMN_STATUS} when the download is waiting to start.
+ */
+ public final static int STATUS_PENDING = 1 << 0;
+
+ /**
+ * Value of {@link #COLUMN_STATUS} when the download is currently running.
+ */
+ public final static int STATUS_RUNNING = 1 << 1;
+
+ /**
+ * Value of {@link #COLUMN_STATUS} when the download is waiting to retry or resume.
+ */
+ public final static int STATUS_PAUSED = 1 << 2;
+
+ /**
+ * Value of {@link #COLUMN_STATUS} when the download has successfully completed.
+ */
+ public final static int STATUS_SUCCESSFUL = 1 << 3;
+
+ /**
+ * Value of {@link #COLUMN_STATUS} when the download has failed (and will not be retried).
+ */
+ public final static int STATUS_FAILED = 1 << 4;
+
+
+ /**
+ * Value of COLUMN_ERROR_CODE when the download has completed with an error that doesn't fit
+ * under any other error code.
+ */
+ public final static int ERROR_UNKNOWN = 1000;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when a storage issue arises which doesn't fit under any
+ * other error code. Use the more specific {@link #ERROR_INSUFFICIENT_SPACE} and
+ * {@link #ERROR_DEVICE_NOT_FOUND} when appropriate.
+ */
+ public final static int ERROR_FILE_ERROR = 1001;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when an HTTP code was received that download manager
+ * can't handle.
+ */
+ public final static int ERROR_UNHANDLED_HTTP_CODE = 1002;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when an error receiving or processing data occurred at
+ * the HTTP level.
+ */
+ public final static int ERROR_HTTP_DATA_ERROR = 1004;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when there were too many redirects.
+ */
+ public final static int ERROR_TOO_MANY_REDIRECTS = 1005;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when there was insufficient storage space. Typically,
+ * this is because the SD card is full.
+ */
+ public final static int ERROR_INSUFFICIENT_SPACE = 1006;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when no external storage device was found. Typically,
+ * this is because the SD card is not mounted.
+ */
+ public final static int ERROR_DEVICE_NOT_FOUND = 1007;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when some possibly transient error occurred but we can't
+ * resume the download.
+ */
+ public final static int ERROR_CANNOT_RESUME = 1008;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when the requested destination file already exists (the
+ * download manager will not overwrite an existing file).
+ */
+ public final static int ERROR_FILE_ALREADY_EXISTS = 1009;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when the download is paused because some network error
+ * occurred and the download manager is waiting before retrying the request.
+ */
+ public final static int PAUSED_WAITING_TO_RETRY = 1;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when the download is waiting for network connectivity to
+ * proceed.
+ */
+ public final static int PAUSED_WAITING_FOR_NETWORK = 2;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when the download exceeds a size limit for downloads over
+ * the mobile network and the download manager is waiting for a Wi-Fi connection to proceed.
+ */
+ public final static int PAUSED_QUEUED_FOR_WIFI = 3;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when the download is paused for some other reason.
+ */
+ public final static int PAUSED_UNKNOWN = 4;
+
+ /**
+ * Broadcast intent action sent by the download manager when a download completes.
+ */
+ public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
+
+ /**
+ * Broadcast intent action sent by the download manager when the user clicks on a running
+ * download, either from a system notification or from the downloads UI.
+ */
+ public final static String ACTION_NOTIFICATION_CLICKED =
+ "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
+
+ /**
+ * Intent action to launch an activity to display all downloads.
+ */
+ public final static String ACTION_VIEW_DOWNLOADS = "android.intent.action.VIEW_DOWNLOADS";
+
+ /**
+ * Intent extra included with {@link #ACTION_DOWNLOAD_COMPLETE} intents, indicating the ID (as a
+ * long) of the download that just completed.
+ */
+ public static final String EXTRA_DOWNLOAD_ID = "extra_download_id";
+
+ // this array must contain all public columns
+ private static final String[] COLUMNS = new String[] {
+ COLUMN_ID,
+ COLUMN_MEDIAPROVIDER_URI,
+ COLUMN_TITLE,
+ COLUMN_DESCRIPTION,
+ COLUMN_URI,
+ COLUMN_MEDIA_TYPE,
+ COLUMN_TOTAL_SIZE_BYTES,
+ COLUMN_LOCAL_URI,
+ COLUMN_STATUS,
+ COLUMN_REASON,
+ COLUMN_BYTES_DOWNLOADED_SO_FAR,
+ COLUMN_LAST_MODIFIED_TIMESTAMP
+ };
+
+ // columns to request from DownloadProvider
+ private static final String[] UNDERLYING_COLUMNS = new String[] {
+ Downloads.Impl._ID,
+ Downloads.Impl.COLUMN_MEDIAPROVIDER_URI,
+ Downloads.COLUMN_TITLE,
+ Downloads.COLUMN_DESCRIPTION,
+ Downloads.COLUMN_URI,
+ Downloads.COLUMN_MIME_TYPE,
+ Downloads.COLUMN_TOTAL_BYTES,
+ Downloads.COLUMN_STATUS,
+ Downloads.COLUMN_CURRENT_BYTES,
+ Downloads.COLUMN_LAST_MODIFICATION,
+ Downloads.COLUMN_DESTINATION,
+ Downloads.Impl.COLUMN_FILE_NAME_HINT,
+ Downloads.Impl._DATA,
+ };
+
+ private static final Set<String> LONG_COLUMNS = new HashSet<String>(
+ Arrays.asList(COLUMN_ID, COLUMN_TOTAL_SIZE_BYTES, COLUMN_STATUS, COLUMN_REASON,
+ COLUMN_BYTES_DOWNLOADED_SO_FAR, COLUMN_LAST_MODIFIED_TIMESTAMP));
+
+ /**
+ * This class contains all the information necessary to request a new download. The URI is the
+ * only required parameter.
+ *
+ * Note that the default download destination is a shared volume where the system might delete
+ * your file if it needs to reclaim space for system use. If this is a problem, use a location
+ * on external storage (see {@link #setDestinationUri(Uri)}.
+ */
+ public static class Request {
+ /**
+ * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
+ * {@link ConnectivityManager#TYPE_MOBILE}.
+ */
+ public static final int NETWORK_MOBILE = 1 << 0;
+
+ /**
+ * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
+ * {@link ConnectivityManager#TYPE_WIFI}.
+ */
+ public static final int NETWORK_WIFI = 1 << 1;
+
+ private Uri mUri;
+ private Uri mDestinationUri;
+ private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
+ private CharSequence mTitle;
+ private CharSequence mDescription;
+ private boolean mShowNotification = true;
+ private String mMimeType;
+ private boolean mRoamingAllowed = true;
+ private int mAllowedNetworkTypes = ~0; // default to all network types allowed
+ private boolean mIsVisibleInDownloadsUi = true;
+
+ /**
+ * @param uri the HTTP URI to download.
+ */
+ public Request(Uri uri) {
+ if (uri == null) {
+ throw new NullPointerException();
+ }
+ String scheme = uri.getScheme();
+ if (scheme == null || !scheme.equals("http")) {
+ throw new IllegalArgumentException("Can only download HTTP URIs: " + uri);
+ }
+ mUri = uri;
+ }
+
+ /**
+ * Set the local destination for the downloaded file. Must be a file URI to a path on
+ * external storage, and the calling application must have the WRITE_EXTERNAL_STORAGE
+ * permission.
+ *
+ * By default, downloads are saved to a generated filename in the shared download cache and
+ * may be deleted by the system at any time to reclaim space.
+ *
+ * @return this object
+ */
+ public Request setDestinationUri(Uri uri) {
+ mDestinationUri = uri;
+ return this;
+ }
+
+ /**
+ * Set the local destination for the downloaded file to a path within the application's
+ * external files directory (as returned by {@link Context#getExternalFilesDir(String)}.
+ *
+ * @param context the {@link Context} to use in determining the external files directory
+ * @param dirType the directory type to pass to {@link Context#getExternalFilesDir(String)}
+ * @param subPath the path within the external directory, including the destination filename
+ * @return this object
+ */
+ public Request setDestinationInExternalFilesDir(Context context, String dirType,
+ String subPath) {
+ setDestinationFromBase(context.getExternalFilesDir(dirType), subPath);
+ return this;
+ }
+
+ /**
+ * Set the local destination for the downloaded file to a path within the public external
+ * storage directory (as returned by
+ * {@link Environment#getExternalStoragePublicDirectory(String)}.
+ *
+ * @param dirType the directory type to pass to
+ * {@link Environment#getExternalStoragePublicDirectory(String)}
+ * @param subPath the path within the external directory, including the destination filename
+ * @return this object
+ */
+ public Request setDestinationInExternalPublicDir(String dirType, String subPath) {
+ setDestinationFromBase(Environment.getExternalStoragePublicDirectory(dirType), subPath);
+ return this;
+ }
+
+ private void setDestinationFromBase(File base, String subPath) {
+ if (subPath == null) {
+ throw new NullPointerException("subPath cannot be null");
+ }
+ mDestinationUri = Uri.withAppendedPath(Uri.fromFile(base), subPath);
+ }
+
+ /**
+ * Add an HTTP header to be included with the download request. The header will be added to
+ * the end of the list.
+ * @param header HTTP header name
+ * @param value header value
+ * @return this object
+ * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2">HTTP/1.1
+ * Message Headers</a>
+ */
+ public Request addRequestHeader(String header, String value) {
+ if (header == null) {
+ throw new NullPointerException("header cannot be null");
+ }
+ if (header.contains(":")) {
+ throw new IllegalArgumentException("header may not contain ':'");
+ }
+ if (value == null) {
+ value = "";
+ }
+ mRequestHeaders.add(Pair.create(header, value));
+ return this;
+ }
+
+ /**
+ * Set the title of this download, to be displayed in notifications (if enabled). If no
+ * title is given, a default one will be assigned based on the download filename, once the
+ * download starts.
+ * @return this object
+ */
+ public Request setTitle(CharSequence title) {
+ mTitle = title;
+ return this;
+ }
+
+ /**
+ * Set a description of this download, to be displayed in notifications (if enabled)
+ * @return this object
+ */
+ public Request setDescription(CharSequence description) {
+ mDescription = description;
+ return this;
+ }
+
+ /**
+ * Set the MIME content type of this download. This will override the content type declared
+ * in the server's response.
+ * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7">HTTP/1.1
+ * Media Types</a>
+ * @return this object
+ */
+ public Request setMimeType(String mimeType) {
+ mMimeType = mimeType;
+ return this;
+ }
+
+ /**
+ * Control whether a system notification is posted by the download manager while this
+ * download is running. If enabled, the download manager posts notifications about downloads
+ * through the system {@link android.app.NotificationManager}. By default, a notification is
+ * shown.
+ *
+ * If set to false, this requires the permission
+ * android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
+ *
+ * @param show whether the download manager should show a notification for this download.
+ * @return this object
+ */
+ public Request setShowRunningNotification(boolean show) {
+ mShowNotification = show;
+ return this;
+ }
+
+ /**
+ * Restrict the types of networks over which this download may proceed. By default, all
+ * network types are allowed.
+ * @param flags any combination of the NETWORK_* bit flags.
+ * @return this object
+ */
+ public Request setAllowedNetworkTypes(int flags) {
+ mAllowedNetworkTypes = flags;
+ return this;
+ }
+
+ /**
+ * Set whether this download may proceed over a roaming connection. By default, roaming is
+ * allowed.
+ * @param allowed whether to allow a roaming connection to be used
+ * @return this object
+ */
+ public Request setAllowedOverRoaming(boolean allowed) {
+ mRoamingAllowed = allowed;
+ return this;
+ }
+
+ /**
+ * Set whether this download should be displayed in the system's Downloads UI. True by
+ * default.
+ * @param isVisible whether to display this download in the Downloads UI
+ * @return this object
+ */
+ public Request setVisibleInDownloadsUi(boolean isVisible) {
+ mIsVisibleInDownloadsUi = isVisible;
+ return this;
+ }
+
+ /**
+ * @return ContentValues to be passed to DownloadProvider.insert()
+ */
+ ContentValues toContentValues(String packageName) {
+ ContentValues values = new ContentValues();
+ assert mUri != null;
+ values.put(Downloads.COLUMN_URI, mUri.toString());
+ values.put(Downloads.Impl.COLUMN_IS_PUBLIC_API, true);
+ values.put(Downloads.COLUMN_NOTIFICATION_PACKAGE, packageName);
+
+ if (mDestinationUri != null) {
+ values.put(Downloads.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
+ values.put(Downloads.COLUMN_FILE_NAME_HINT, mDestinationUri.toString());
+ } else {
+ values.put(Downloads.COLUMN_DESTINATION,
+ Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE);
+ }
+
+ if (!mRequestHeaders.isEmpty()) {
+ encodeHttpHeaders(values);
+ }
+
+ putIfNonNull(values, Downloads.COLUMN_TITLE, mTitle);
+ putIfNonNull(values, Downloads.COLUMN_DESCRIPTION, mDescription);
+ putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMimeType);
+
+ values.put(Downloads.COLUMN_VISIBILITY,
+ mShowNotification ? Downloads.VISIBILITY_VISIBLE
+ : Downloads.VISIBILITY_HIDDEN);
+
+ values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes);
+ values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
+ values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, mIsVisibleInDownloadsUi);
+
+ return values;
+ }
+
+ private void encodeHttpHeaders(ContentValues values) {
+ int index = 0;
+ for (Pair<String, String> header : mRequestHeaders) {
+ String headerString = header.first + ": " + header.second;
+ values.put(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX + index, headerString);
+ index++;
+ }
+ }
+
+ private void putIfNonNull(ContentValues contentValues, String key, Object value) {
+ if (value != null) {
+ contentValues.put(key, value.toString());
+ }
+ }
+ }
+
+ /**
+ * This class may be used to filter download manager queries.
+ */
+ public static class Query {
+ /**
+ * Constant for use with {@link #orderBy}
+ * @hide
+ */
+ public static final int ORDER_ASCENDING = 1;
+
+ /**
+ * Constant for use with {@link #orderBy}
+ * @hide
+ */
+ public static final int ORDER_DESCENDING = 2;
+
+ private long[] mIds = null;
+ private Integer mStatusFlags = null;
+ private String mOrderByColumn = Downloads.COLUMN_LAST_MODIFICATION;
+ private int mOrderDirection = ORDER_DESCENDING;
+ private boolean mOnlyIncludeVisibleInDownloadsUi = false;
+
+ /**
+ * Include only the downloads with the given IDs.
+ * @return this object
+ */
+ public Query setFilterById(long... ids) {
+ mIds = ids;
+ return this;
+ }
+
+ /**
+ * Include only downloads with status matching any the given status flags.
+ * @param flags any combination of the STATUS_* bit flags
+ * @return this object
+ */
+ public Query setFilterByStatus(int flags) {
+ mStatusFlags = flags;
+ return this;
+ }
+
+ /**
+ * Controls whether this query includes downloads not visible in the system's Downloads UI.
+ * @param value if true, this query will only include downloads that should be displayed in
+ * the system's Downloads UI; if false (the default), this query will include
+ * both visible and invisible downloads.
+ * @return this object
+ * @hide
+ */
+ public Query setOnlyIncludeVisibleInDownloadsUi(boolean value) {
+ mOnlyIncludeVisibleInDownloadsUi = value;
+ return this;
+ }
+
+ /**
+ * Change the sort order of the returned Cursor.
+ *
+ * @param column one of the COLUMN_* constants; currently, only
+ * {@link #COLUMN_LAST_MODIFIED_TIMESTAMP} and {@link #COLUMN_TOTAL_SIZE_BYTES} are
+ * supported.
+ * @param direction either {@link #ORDER_ASCENDING} or {@link #ORDER_DESCENDING}
+ * @return this object
+ * @hide
+ */
+ public Query orderBy(String column, int direction) {
+ if (direction != ORDER_ASCENDING && direction != ORDER_DESCENDING) {
+ throw new IllegalArgumentException("Invalid direction: " + direction);
+ }
+
+ if (column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP)) {
+ mOrderByColumn = Downloads.COLUMN_LAST_MODIFICATION;
+ } else if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) {
+ mOrderByColumn = Downloads.COLUMN_TOTAL_BYTES;
+ } else {
+ throw new IllegalArgumentException("Cannot order by " + column);
+ }
+ mOrderDirection = direction;
+ return this;
+ }
+
+ /**
+ * Run this query using the given ContentResolver.
+ * @param projection the projection to pass to ContentResolver.query()
+ * @return the Cursor returned by ContentResolver.query()
+ */
+ Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) {
+ Uri uri = baseUri;
+ List<String> selectionParts = new ArrayList<String>();
+ String[] selectionArgs = null;
+
+ if (mIds != null) {
+ selectionParts.add(getWhereClauseForIds(mIds));
+ selectionArgs = getWhereArgsForIds(mIds);
+ }
+
+ if (mStatusFlags != null) {
+ List<String> parts = new ArrayList<String>();
+ if ((mStatusFlags & STATUS_PENDING) != 0) {
+ parts.add(statusClause("=", Downloads.STATUS_PENDING));
+ }
+ if ((mStatusFlags & STATUS_RUNNING) != 0) {
+ parts.add(statusClause("=", Downloads.STATUS_RUNNING));
+ }
+ if ((mStatusFlags & STATUS_PAUSED) != 0) {
+ parts.add(statusClause("=", Downloads.Impl.STATUS_PAUSED_BY_APP));
+ parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_TO_RETRY));
+ parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_FOR_NETWORK));
+ parts.add(statusClause("=", Downloads.Impl.STATUS_QUEUED_FOR_WIFI));
+ }
+ if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) {
+ parts.add(statusClause("=", Downloads.STATUS_SUCCESS));
+ }
+ if ((mStatusFlags & STATUS_FAILED) != 0) {
+ parts.add("(" + statusClause(">=", 400)
+ + " AND " + statusClause("<", 600) + ")");
+ }
+ selectionParts.add(joinStrings(" OR ", parts));
+ }
+
+ if (mOnlyIncludeVisibleInDownloadsUi) {
+ selectionParts.add(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI + " != '0'");
+ }
+
+ // only return rows which are not marked 'deleted = 1'
+ selectionParts.add(Downloads.Impl.COLUMN_DELETED + " != '1'");
+
+ String selection = joinStrings(" AND ", selectionParts);
+ String orderDirection = (mOrderDirection == ORDER_ASCENDING ? "ASC" : "DESC");
+ String orderBy = mOrderByColumn + " " + orderDirection;
+
+ return resolver.query(uri, projection, selection, selectionArgs, orderBy);
+ }
+
+ private String joinStrings(String joiner, Iterable<String> parts) {
+ StringBuilder builder = new StringBuilder();
+ boolean first = true;
+ for (String part : parts) {
+ if (!first) {
+ builder.append(joiner);
+ }
+ builder.append(part);
+ first = false;
+ }
+ return builder.toString();
+ }
+
+ private String statusClause(String operator, int value) {
+ return Downloads.COLUMN_STATUS + operator + "'" + value + "'";
+ }
+ }
+
+ private ContentResolver mResolver;
+ private String mPackageName;
+ private Uri mBaseUri = Downloads.Impl.CONTENT_URI;
+
+ /**
+ * @hide
+ */
+ public DownloadManager(ContentResolver resolver, String packageName) {
+ mResolver = resolver;
+ mPackageName = packageName;
+ }
+
+ /**
+ * Makes this object access the download provider through /all_downloads URIs rather than
+ * /my_downloads URIs, for clients that have permission to do so.
+ * @hide
+ */
+ public void setAccessAllDownloads(boolean accessAllDownloads) {
+ if (accessAllDownloads) {
+ mBaseUri = Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI;
+ } else {
+ mBaseUri = Downloads.Impl.CONTENT_URI;
+ }
+ }
+
+ /**
+ * Enqueue a new download. The download will start automatically once the download manager is
+ * ready to execute it and connectivity is available.
+ *
+ * @param request the parameters specifying this download
+ * @return an ID for the download, unique across the system. This ID is used to make future
+ * calls related to this download.
+ */
+ public long enqueue(Request request) {
+ ContentValues values = request.toContentValues(mPackageName);
+ Uri downloadUri = mResolver.insert(Downloads.CONTENT_URI, values);
+ long id = Long.parseLong(downloadUri.getLastPathSegment());
+ return id;
+ }
+
+ /**
+ * Marks the specified download as 'to be deleted'. This is done when a completed download
+ * is to be removed but the row was stored without enough info to delete the corresponding
+ * metadata from Mediaprovider database. Actual cleanup of this row is done in DownloadService.
+ *
+ * @param ids the IDs of the downloads to be marked 'deleted'
+ * @return the number of downloads actually updated
+ * @hide
+ */
+ public int markRowDeleted(long... ids) {
+ if (ids == null || ids.length == 0) {
+ // called with nothing to remove!
+ throw new IllegalArgumentException("input param 'ids' can't be null");
+ }
+ ContentValues values = new ContentValues();
+ values.put(Downloads.Impl.COLUMN_DELETED, 1);
+ return mResolver.update(mBaseUri, values, getWhereClauseForIds(ids),
+ getWhereArgsForIds(ids));
+ }
+
+ /**
+ * Cancel downloads and remove them from the download manager. Each download will be stopped if
+ * it was running, and it will no longer be accessible through the download manager. If a file
+ * was already downloaded to external storage, it will not be deleted.
+ *
+ * @param ids the IDs of the downloads to remove
+ * @return the number of downloads actually removed
+ */
+ public int remove(long... ids) {
+ if (ids == null || ids.length == 0) {
+ // called with nothing to remove!
+ throw new IllegalArgumentException("input param 'ids' can't be null");
+ }
+ return mResolver.delete(mBaseUri, getWhereClauseForIds(ids), getWhereArgsForIds(ids));
+ }
+
+ /**
+ * Query the download manager about downloads that have been requested.
+ * @param query parameters specifying filters for this query
+ * @return a Cursor over the result set of downloads, with columns consisting of all the
+ * COLUMN_* constants.
+ */
+ public Cursor query(Query query) {
+ Cursor underlyingCursor = query.runQuery(mResolver, UNDERLYING_COLUMNS, mBaseUri);
+ if (underlyingCursor == null) {
+ return null;
+ }
+ return new CursorTranslator(underlyingCursor, mBaseUri);
+ }
+
+ /**
+ * Open a downloaded file for reading. The download must have completed.
+ * @param id the ID of the download
+ * @return a read-only {@link ParcelFileDescriptor}
+ * @throws FileNotFoundException if the destination file does not already exist
+ */
+ public ParcelFileDescriptor openDownloadedFile(long id) throws FileNotFoundException {
+ return mResolver.openFileDescriptor(getDownloadUri(id), "r");
+ }
+
+ /**
+ * Restart the given downloads, which must have already completed (successfully or not). This
+ * method will only work when called from within the download manager's process.
+ * @param ids the IDs of the downloads
+ * @hide
+ */
+ public void restartDownload(long... ids) {
+ Cursor cursor = query(new Query().setFilterById(ids));
+ try {
+ for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ int status = cursor.getInt(cursor.getColumnIndex(COLUMN_STATUS));
+ if (status != STATUS_SUCCESSFUL && status != STATUS_FAILED) {
+ throw new IllegalArgumentException("Cannot restart incomplete download: "
+ + cursor.getLong(cursor.getColumnIndex(COLUMN_ID)));
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+
+ ContentValues values = new ContentValues();
+ values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
+ values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
+ values.putNull(Downloads.Impl._DATA);
+ values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
+ mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids));
+ }
+
+ /**
+ * Get the DownloadProvider URI for the download with the given ID.
+ */
+ Uri getDownloadUri(long id) {
+ return ContentUris.withAppendedId(mBaseUri, id);
+ }
+
+ /**
+ * Get a parameterized SQL WHERE clause to select a bunch of IDs.
+ */
+ static String getWhereClauseForIds(long[] ids) {
+ StringBuilder whereClause = new StringBuilder();
+ whereClause.append("(");
+ for (int i = 0; i < ids.length; i++) {
+ if (i > 0) {
+ whereClause.append("OR ");
+ }
+ whereClause.append(Downloads.Impl._ID);
+ whereClause.append(" = ? ");
+ }
+ whereClause.append(")");
+ return whereClause.toString();
+ }
+
+ /**
+ * Get the selection args for a clause returned by {@link #getWhereClauseForIds(long[])}.
+ */
+ static String[] getWhereArgsForIds(long[] ids) {
+ String[] whereArgs = new String[ids.length];
+ for (int i = 0; i < ids.length; i++) {
+ whereArgs[i] = Long.toString(ids[i]);
+ }
+ return whereArgs;
+ }
+
+ /**
+ * This class wraps a cursor returned by DownloadProvider -- the "underlying cursor" -- and
+ * presents a different set of columns, those defined in the DownloadManager.COLUMN_* constants.
+ * Some columns correspond directly to underlying values while others are computed from
+ * underlying data.
+ */
+ private static class CursorTranslator extends CursorWrapper {
+ private Uri mBaseUri;
+
+ public CursorTranslator(Cursor cursor, Uri baseUri) {
+ super(cursor);
+ mBaseUri = baseUri;
+ }
+
+ @Override
+ public int getColumnIndex(String columnName) {
+ return Arrays.asList(COLUMNS).indexOf(columnName);
+ }
+
+ @Override
+ public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
+ int index = getColumnIndex(columnName);
+ if (index == -1) {
+ throw new IllegalArgumentException("No such column: " + columnName);
+ }
+ return index;
+ }
+
+ @Override
+ public String getColumnName(int columnIndex) {
+ int numColumns = COLUMNS.length;
+ if (columnIndex < 0 || columnIndex >= numColumns) {
+ throw new IllegalArgumentException("Invalid column index " + columnIndex + ", "
+ + numColumns + " columns exist");
+ }
+ return COLUMNS[columnIndex];
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ String[] returnColumns = new String[COLUMNS.length];
+ System.arraycopy(COLUMNS, 0, returnColumns, 0, COLUMNS.length);
+ return returnColumns;
+ }
+
+ @Override
+ public int getColumnCount() {
+ return COLUMNS.length;
+ }
+
+ @Override
+ public byte[] getBlob(int columnIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public double getDouble(int columnIndex) {
+ return getLong(columnIndex);
+ }
+
+ private boolean isLongColumn(String column) {
+ return LONG_COLUMNS.contains(column);
+ }
+
+ @Override
+ public float getFloat(int columnIndex) {
+ return (float) getDouble(columnIndex);
+ }
+
+ @Override
+ public int getInt(int columnIndex) {
+ return (int) getLong(columnIndex);
+ }
+
+ @Override
+ public long getLong(int columnIndex) {
+ return translateLong(getColumnName(columnIndex));
+ }
+
+ @Override
+ public short getShort(int columnIndex) {
+ return (short) getLong(columnIndex);
+ }
+
+ @Override
+ public String getString(int columnIndex) {
+ return translateString(getColumnName(columnIndex));
+ }
+
+ private String translateString(String column) {
+ if (isLongColumn(column)) {
+ return Long.toString(translateLong(column));
+ }
+ if (column.equals(COLUMN_TITLE)) {
+ return getUnderlyingString(Downloads.COLUMN_TITLE);
+ }
+ if (column.equals(COLUMN_DESCRIPTION)) {
+ return getUnderlyingString(Downloads.COLUMN_DESCRIPTION);
+ }
+ if (column.equals(COLUMN_URI)) {
+ return getUnderlyingString(Downloads.COLUMN_URI);
+ }
+ if (column.equals(COLUMN_MEDIA_TYPE)) {
+ return getUnderlyingString(Downloads.COLUMN_MIME_TYPE);
+ }
+ if (column.equals(COLUMN_MEDIAPROVIDER_URI)) {
+ return getUnderlyingString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI);
+ }
+
+ assert column.equals(COLUMN_LOCAL_URI);
+ return getLocalUri();
+ }
+
+ private String getLocalUri() {
+ long destinationType = getUnderlyingLong(Downloads.Impl.COLUMN_DESTINATION);
+ if (destinationType == Downloads.Impl.DESTINATION_FILE_URI) {
+ // return client-provided file URI for external download
+ return getUnderlyingString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
+ }
+
+ if (destinationType == Downloads.Impl.DESTINATION_EXTERNAL) {
+ // return stored destination for legacy external download
+ String localPath = getUnderlyingString(Downloads.Impl._DATA);
+ if (localPath == null) {
+ return null;
+ }
+ return Uri.fromFile(new File(localPath)).toString();
+ }
+
+ // return content URI for cache download
+ long downloadId = getUnderlyingLong(Downloads.Impl._ID);
+ return ContentUris.withAppendedId(mBaseUri, downloadId).toString();
+ }
+
+ private long translateLong(String column) {
+ if (!isLongColumn(column)) {
+ // mimic behavior of underlying cursor -- most likely, throw NumberFormatException
+ return Long.valueOf(translateString(column));
+ }
+
+ if (column.equals(COLUMN_ID)) {
+ return getUnderlyingLong(Downloads.Impl._ID);
+ }
+ if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) {
+ return getUnderlyingLong(Downloads.COLUMN_TOTAL_BYTES);
+ }
+ if (column.equals(COLUMN_STATUS)) {
+ return translateStatus((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
+ }
+ if (column.equals(COLUMN_REASON)) {
+ return getReason((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
+ }
+ if (column.equals(COLUMN_BYTES_DOWNLOADED_SO_FAR)) {
+ return getUnderlyingLong(Downloads.COLUMN_CURRENT_BYTES);
+ }
+ assert column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP);
+ return getUnderlyingLong(Downloads.COLUMN_LAST_MODIFICATION);
+ }
+
+ private long getReason(int status) {
+ switch (translateStatus(status)) {
+ case STATUS_FAILED:
+ return getErrorCode(status);
+
+ case STATUS_PAUSED:
+ return getPausedReason(status);
+
+ default:
+ return 0; // arbitrary value when status is not an error
+ }
+ }
+
+ private long getPausedReason(int status) {
+ switch (status) {
+ case Downloads.Impl.STATUS_WAITING_TO_RETRY:
+ return PAUSED_WAITING_TO_RETRY;
+
+ case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
+ return PAUSED_WAITING_FOR_NETWORK;
+
+ case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
+ return PAUSED_QUEUED_FOR_WIFI;
+
+ default:
+ return PAUSED_UNKNOWN;
+ }
+ }
+
+ private long getErrorCode(int status) {
+ if ((400 <= status && status < Downloads.Impl.MIN_ARTIFICIAL_ERROR_STATUS)
+ || (500 <= status && status < 600)) {
+ // HTTP status code
+ return status;
+ }
+
+ switch (status) {
+ case Downloads.STATUS_FILE_ERROR:
+ return ERROR_FILE_ERROR;
+
+ case Downloads.STATUS_UNHANDLED_HTTP_CODE:
+ case Downloads.STATUS_UNHANDLED_REDIRECT:
+ return ERROR_UNHANDLED_HTTP_CODE;
+
+ case Downloads.STATUS_HTTP_DATA_ERROR:
+ return ERROR_HTTP_DATA_ERROR;
+
+ case Downloads.STATUS_TOO_MANY_REDIRECTS:
+ return ERROR_TOO_MANY_REDIRECTS;
+
+ case Downloads.STATUS_INSUFFICIENT_SPACE_ERROR:
+ return ERROR_INSUFFICIENT_SPACE;
+
+ case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
+ return ERROR_DEVICE_NOT_FOUND;
+
+ case Downloads.Impl.STATUS_CANNOT_RESUME:
+ return ERROR_CANNOT_RESUME;
+
+ case Downloads.Impl.STATUS_FILE_ALREADY_EXISTS_ERROR:
+ return ERROR_FILE_ALREADY_EXISTS;
+
+ default:
+ return ERROR_UNKNOWN;
+ }
+ }
+
+ private long getUnderlyingLong(String column) {
+ return super.getLong(super.getColumnIndex(column));
+ }
+
+ private String getUnderlyingString(String column) {
+ return super.getString(super.getColumnIndex(column));
+ }
+
+ private int translateStatus(int status) {
+ switch (status) {
+ case Downloads.STATUS_PENDING:
+ return STATUS_PENDING;
+
+ case Downloads.STATUS_RUNNING:
+ return STATUS_RUNNING;
+
+ case Downloads.Impl.STATUS_PAUSED_BY_APP:
+ case Downloads.Impl.STATUS_WAITING_TO_RETRY:
+ case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
+ case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
+ return STATUS_PAUSED;
+
+ case Downloads.STATUS_SUCCESS:
+ return STATUS_SUCCESSFUL;
+
+ default:
+ assert Downloads.isStatusError(status);
+ return STATUS_FAILED;
+ }
+ }
+ }
+}
View
549 src/frameworks/base/core/java/android/app/Notification.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import java.util.Date;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.widget.RemoteViews;
+
+/**
+ * A class that represents how a persistent notification is to be presented to
+ * the user using the {@link android.app.NotificationManager}.
+ *
+ * <p>For a guide to creating notifications, see the
+ * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Creating Status
+ * Bar Notifications</a> document in the Dev Guide.</p>
+ */
+public class Notification implements Parcelable
+{
+ /**
+ * Use all default values (where applicable).
+ */
+ public static final int DEFAULT_ALL = ~0;
+
+ /**
+ * Use the default notification sound. This will ignore any given
+ * {@link #sound}.
+ *
+ * @see #defaults
+ */
+ public static final int DEFAULT_SOUND = 1;
+
+ /**
+ * Use the default notification vibrate. This will ignore any given
+ * {@link #vibrate}. Using phone vibration requires the
+ * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
+ *
+ * @see #defaults
+ */
+ public static final int DEFAULT_VIBRATE = 2;
+
+ /**
+ * Use the default notification lights. This will ignore the
+ * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
+ * {@link #ledOnMS}.
+ *
+ * @see #defaults
+ */
+ public static final int DEFAULT_LIGHTS = 4;
+
+ /**
+ * The timestamp for the notification. The icons and expanded views
+ * are sorted by this key.
+ */
+ public long when;
+
+ /**
+ * The resource id of a drawable to use as the icon in the status bar.
+ */
+ public int icon;
+
+ /**
+ * The number of events that this notification represents. For example, in a new mail
+ * notification, this could be the number of unread messages. This number is superimposed over
+ * the icon in the status bar. If the number is 0 or negative, it is not shown in the status
+ * bar.
+ */
+ public int number;
+
+ /**
+ * The intent to execute when the expanded status entry is clicked. If
+ * this is an activity, it must include the
+ * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
+ * that you take care of task management as described in the <em>Activities and Tasks</em>
+ * section of the <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application
+ * Fundamentals</a> document.