Merge change 1642 into donut

* changes:
  Allow the build system to build Objective-C sources.
diff --git a/cleanspec.mk b/cleanspec.mk
index 50a1f1f..14c8016 100644
--- a/cleanspec.mk
+++ b/cleanspec.mk
@@ -78,6 +78,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj)
 $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/tcpdump)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/location)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/core/Makefile b/core/Makefile
index fa0550c..110b3c9 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -131,6 +131,7 @@
 			TARGET_BOOTLOADER_BOARD_NAME="$(TARGET_BOOTLOADER_BOARD_NAME)" \
 			BUILD_FINGERPRINT="$(BUILD_FINGERPRINT)" \
 			TARGET_BOARD_PLATFORM="$(TARGET_BOARD_PLATFORM)" \
+			TARGET_CPU_ABI="$(TARGET_CPU_ABI)" \
 	        bash $(BUILDINFO_SH) > $@
 	$(hide) if [ -f $(TARGET_DEVICE_DIR)/system.prop ]; then \
 	          cat $(TARGET_DEVICE_DIR)/system.prop >> $@; \
diff --git a/core/apicheck_msg_current.txt b/core/apicheck_msg_current.txt
index c277ecd..d723a19 100644
--- a/core/apicheck_msg_current.txt
+++ b/core/apicheck_msg_current.txt
@@ -6,12 +6,11 @@
    1) You can add "@hide" javadoc comments to the methods, etc. listed in the
       errors above.
 
-   2) You can update current.xml by executing the following commands:
+   2) You can update current.xml by executing the following command:
 
-         p4 edit frameworks/base/api/current.xml
          make update-api
 
-      To check in the revised current.xml, you will need OWNERS approval.
+      To check in the revised current.xml, you will need approval from the android API council.
 ******************************
 
 
diff --git a/core/main.mk b/core/main.mk
index 655a592..2bf8102 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -406,6 +406,10 @@
 
 # Clean up/verify variables defined by the board config file.
 TARGET_BOOTLOADER_BOARD_NAME := $(strip $(TARGET_BOOTLOADER_BOARD_NAME))
+TARGET_CPU_ABI := $(strip $(TARGET_CPU_ABI))
+ifeq ($(TARGET_CPU_ABI),)
+  $(error No TARGET_CPU_ABI defined by board config: $(board_config_mk))
+endif
 
 #
 # Include all of the makefiles in the system
diff --git a/core/pathmap.mk b/core/pathmap.mk
index 13cb80d..de7c1bb 100644
--- a/core/pathmap.mk
+++ b/core/pathmap.mk
@@ -82,6 +82,7 @@
 	    opengl \
 	    sax \
 	    telephony \
+	    tts \
 	    wifi \
 	 )
 
diff --git a/target/board/generic/BoardConfig.mk b/target/board/generic/BoardConfig.mk
index a874742..6ec2de3 100644
--- a/target/board/generic/BoardConfig.mk
+++ b/target/board/generic/BoardConfig.mk
@@ -7,5 +7,6 @@
 TARGET_NO_BOOTLOADER := true
 TARGET_NO_KERNEL := true
 TARGET_NO_RADIOIMAGE := true
+TARGET_CPU_ABI := armeabi
 HAVE_HTC_AUDIO_DRIVER := true
 BOARD_USES_GENERIC_AUDIO := true
diff --git a/target/board/sim/BoardConfig.mk b/target/board/sim/BoardConfig.mk
index 92679d9..491b30f 100644
--- a/target/board/sim/BoardConfig.mk
+++ b/target/board/sim/BoardConfig.mk
@@ -17,6 +17,9 @@
 # Don't bother with a kernel
 TARGET_NO_KERNEL := true
 
+# The simulator does not support native code at all
+TARGET_CPU_ABI := none
+
 #the simulator partially emulates the original HTC /dev/eac audio interface
 HAVE_HTC_AUDIO_DRIVER := true
 BOARD_USES_GENERIC_AUDIO := true
diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh
index 1fcac76..5c738a2 100755
--- a/tools/buildinfo.sh
+++ b/tools/buildinfo.sh
@@ -20,6 +20,7 @@
 echo "ro.product.name=$PRODUCT_NAME"
 echo "ro.product.device=$TARGET_DEVICE"
 echo "ro.product.board=$TARGET_BOOTLOADER_BOARD_NAME"
+echo "ro.product.cpu.abi=$TARGET_CPU_ABI"
 echo "ro.product.manufacturer=$PRODUCT_MANUFACTURER"
 echo "ro.product.locale.language=$PRODUCT_DEFAULT_LANGUAGE"
 echo "ro.product.locale.region=$PRODUCT_DEFAULT_REGION"
diff --git a/tools/droiddoc/templates-pdk/customization.cs b/tools/droiddoc/templates-pdk/customization.cs
index 01b6e96..563af1e 100644
--- a/tools/droiddoc/templates-pdk/customization.cs
+++ b/tools/droiddoc/templates-pdk/customization.cs
@@ -5,7 +5,7 @@
 def:custom_masthead() ?>
   <div id="header">
       <div id="headerLeft">
-          <a href="<?cs var:toroot ?>index.html" tabindex="-1"><img
+          <a href="<?cs var:toroot ?>guide/index.html" tabindex="-1"><img
               src="<?cs var:toroot ?>assets/images/open_source.png" alt="Open Source Project: Platform Development Kit" /></a>
           <ul class="<?cs 
                   if:reference ?> <?cs
@@ -15,7 +15,7 @@
                   elif:community ?> <?cs
                   elif:publish ?> <?cs
                   elif:about ?> <?cs /if ?>">
-              <li id="guide-link"><a href="<?cs var:toroot ?>index.html"
+              <li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html"
                                   onClick="return loadLast('guide)'"><span>Porting Guide</span></a></li>
               <li id="opensource-link"><a href="http://source.android.com/"
 				 onClick="return loadLast('open')"><span>Open Source</span></a></li>
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index a512ff8..51a6d8f 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import errno
 import getopt
 import getpass
 import os
@@ -132,13 +133,14 @@
   those which require them.  Return a {key: password} dict.  password
   will be None if the key has no password."""
 
-  key_passwords = {}
+  no_passwords = []
+  need_passwords = []
   devnull = open("/dev/null", "w+b")
   for k in sorted(keylist):
     # An empty-string key is used to mean don't re-sign this package.
     # Obviously we don't need a password for this non-key.
     if not k:
-      key_passwords[k] = None
+      no_passwords.append(k)
       continue
 
     p = subprocess.Popen(["openssl", "pkcs8", "-in", k+".pk8",
@@ -148,12 +150,13 @@
                          stderr=subprocess.STDOUT)
     p.communicate()
     if p.returncode == 0:
-      print "%s.pk8 does not require a password" % (k,)
-      key_passwords[k] = None
+      no_passwords.append(k)
     else:
-      key_passwords[k] = getpass.getpass("Enter password for %s.pk8> " % (k,))
+      need_passwords.append(k)
   devnull.close()
-  print
+
+  key_passwords = PasswordManager().GetPasswords(need_passwords)
+  key_passwords.update(dict.fromkeys(no_passwords, None))
   return key_passwords
 
 
@@ -278,3 +281,102 @@
       shutil.rmtree(i)
     else:
       os.remove(i)
+
+
+class PasswordManager(object):
+  def __init__(self):
+    self.editor = os.getenv("EDITOR", None)
+    self.pwfile = os.getenv("ANDROID_PW_FILE", None)
+
+  def GetPasswords(self, items):
+    """Get passwords corresponding to each string in 'items',
+    returning a dict.  (The dict may have keys in addition to the
+    values in 'items'.)
+
+    Uses the passwords in $ANDROID_PW_FILE if available, letting the
+    user edit that file to add more needed passwords.  If no editor is
+    available, or $ANDROID_PW_FILE isn't define, prompts the user
+    interactively in the ordinary way.
+    """
+
+    current = self.ReadFile()
+
+    first = True
+    while True:
+      missing = []
+      for i in items:
+        if i not in current or not current[i]:
+          missing.append(i)
+      # Are all the passwords already in the file?
+      if not missing: return current
+
+      for i in missing:
+        current[i] = ""
+
+      if not first:
+        print "key file %s still missing some passwords." % (self.pwfile,)
+        answer = raw_input("try to edit again? [y]> ").strip()
+        if answer and answer[0] not in 'yY':
+          raise RuntimeError("key passwords unavailable")
+      first = False
+
+      current = self.UpdateAndReadFile(current)
+
+  def PromptResult(self, current):
+    """Prompt the user to enter a value (password) for each key in
+    'current' whose value is fales.  Returns a new dict with all the
+    values.
+    """
+    result = {}
+    for k, v in sorted(current.iteritems()):
+      if v:
+        result[k] = v
+      else:
+        while True:
+          result[k] = getpass.getpass("Enter password for %s key> "
+                                      % (k,)).strip()
+          if result[k]: break
+    return result
+
+  def UpdateAndReadFile(self, current):
+    if not self.editor or not self.pwfile:
+      return self.PromptResult(current)
+
+    f = open(self.pwfile, "w")
+    os.chmod(self.pwfile, 0600)
+    f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
+    f.write("# (Additional spaces are harmless.)\n\n")
+
+    first_line = None
+    sorted = [(not v, k, v) for (k, v) in current.iteritems()]
+    sorted.sort()
+    for i, (_, k, v) in enumerate(sorted):
+      f.write("[[[  %s  ]]] %s\n" % (v, k))
+      if not v and first_line is None:
+        # position cursor on first line with no password.
+        first_line = i + 4
+    f.close()
+
+    p = Run([self.editor, "+%d" % (first_line,), self.pwfile])
+    _, _ = p.communicate()
+
+    return self.ReadFile()
+
+  def ReadFile(self):
+    result = {}
+    if self.pwfile is None: return result
+    try:
+      f = open(self.pwfile, "r")
+      for line in f:
+        line = line.strip()
+        if not line or line[0] == '#': continue
+        m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
+        if not m:
+          print "failed to parse password file: ", line
+        else:
+          result[m.group(2)] = m.group(1)
+      f.close()
+    except IOError, e:
+      if e.errno != errno.ENOENT:
+        print "error reading password file: ", str(e)
+    return result
diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files
index 69e9f5b..364f751 100755
--- a/tools/releasetools/ota_from_target_files
+++ b/tools/releasetools/ota_from_target_files
@@ -42,6 +42,9 @@
       the build scripts (used for developer OTA packages which
       legitimately need to go back and forth).
 
+  -e  (--extra_script)  <file>
+      Insert the contents of file at the end of the update script.
+
 """
 
 import sys
@@ -69,6 +72,7 @@
 OPTIONS.patch_threshold = 0.95
 OPTIONS.wipe_user_data = False
 OPTIONS.omit_prereq = False
+OPTIONS.extra_script = None
 
 def MostPopularKey(d, default):
   """Given a dict, return the key corresponding to the largest
@@ -363,6 +367,9 @@
   script.append("write_raw_image PACKAGE:boot.img BOOT:")
   script.append("show_progress 0.2 10")
 
+  if OPTIONS.extra_script is not None:
+    script.append(OPTIONS.extra_script)
+
   AddScript(script, output_zip)
 
 
@@ -612,6 +619,9 @@
     script.append("show_progress 0.1 5")
     script.append("write_raw_image PACKAGE:boot.img BOOT:")
 
+  if OPTIONS.extra_script is not None:
+    script.append(OPTIONS.extra_script)
+
   AddScript(script, output_zip)
 
 
@@ -628,17 +638,20 @@
       OPTIONS.wipe_user_data = True
     elif o in ("-n", "--no_prereq"):
       OPTIONS.omit_prereq = True
+    elif o in ("-e", "--extra_script"):
+      OPTIONS.extra_script = a
     else:
       return False
     return True
 
   args = common.ParseOptions(argv, __doc__,
-                             extra_opts="b:k:i:d:wn",
+                             extra_opts="b:k:i:d:wne:",
                              extra_long_opts=["board_config=",
                                               "package_key=",
                                               "incremental_from=",
                                               "wipe_user_data",
-                                              "no_prereq"],
+                                              "no_prereq",
+                                              "extra_script="],
                              extra_option_handler=option_handler)
 
   if len(args) != 2:
@@ -652,6 +665,9 @@
     print "  images don't exceed partition sizes."
     print
 
+  if OPTIONS.extra_script is not None:
+    OPTIONS.extra_script = open(OPTIONS.extra_script).read()
+
   print "unzipping target target-files..."
   OPTIONS.input_tmp = common.UnzipTemp(args[0])
   OPTIONS.target_tmp = OPTIONS.input_tmp
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index b3bfaee..9f393c8 100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -101,6 +101,85 @@
   return certmap
 
 
+def CheckAllApksSigned(input_tf_zip, apk_key_map):
+  """Check that all the APKs we want to sign have keys specified, and
+  error out if they don't."""
+  unknown_apks = []
+  for info in input_tf_zip.infolist():
+    if info.filename.endswith(".apk"):
+      name = os.path.basename(info.filename)
+      if name not in apk_key_map:
+        unknown_apks.append(name)
+  if unknown_apks:
+    print "ERROR: no key specified for:\n\n ",
+    print "\n  ".join(unknown_apks)
+    print "\nUse '-e <apkname>=' to specify a key (which may be an"
+    print "empty string to not sign this apk)."
+    sys.exit(1)
+
+
+def SharedUserForApk(data):
+  tmp = tempfile.NamedTemporaryFile()
+  tmp.write(data)
+  tmp.flush()
+
+  p = common.Run(["aapt", "dump", "xmltree", tmp.name, "AndroidManifest.xml"],
+                 stdout=subprocess.PIPE)
+  data, _ = p.communicate()
+  if p.returncode != 0:
+    raise ExternalError("failed to run aapt dump")
+  lines = data.split("\n")
+  for i in lines:
+    m = re.match(r'^\s*A: android:sharedUserId\([0-9a-fx]*\)="([^"]*)" .*$', i)
+    if m:
+      return m.group(1)
+  return None
+
+
+def CheckSharedUserIdsConsistent(input_tf_zip, apk_key_map):
+  """Check that all packages that request the same shared user id are
+  going to be signed with the same key."""
+
+  shared_user_apks = {}
+  maxlen = len("(unknown key)")
+
+  for info in input_tf_zip.infolist():
+    if info.filename.endswith(".apk"):
+      data = input_tf_zip.read(info.filename)
+
+      name = os.path.basename(info.filename)
+      shared_user = SharedUserForApk(data)
+      key = apk_key_map[name]
+      maxlen = max(maxlen, len(key))
+
+      if shared_user is not None:
+        shared_user_apks.setdefault(
+            shared_user, {}).setdefault(key, []).append(name)
+
+  errors = []
+  for k, v in shared_user_apks.iteritems():
+    # each shared user should have exactly one key used for all the
+    # apks that want that user.
+    if len(v) > 1:
+      errors.append((k, v))
+
+  if not errors: return
+
+  print "ERROR:  shared user inconsistency.  All apks wanting to use"
+  print "        a given shared user must be signed with the same key."
+  print
+  errors.sort()
+  for user, keys in errors:
+    print 'shared user id "%s":' % (user,)
+    for key, apps in keys.iteritems():
+      print '  %-*s   %s' % (maxlen, key or "(unknown key)", apps[0])
+      for a in apps[1:]:
+        print (' ' * (maxlen+5)) + a
+    print
+
+  sys.exit(1)
+
+
 def SignApk(data, keyname, pw):
   unsigned = tempfile.NamedTemporaryFile()
   unsigned.write(data)
@@ -117,31 +196,11 @@
   return data
 
 
-def SignApks(input_tf_zip, output_tf_zip):
-  apk_key_map = GetApkCerts(input_tf_zip)
-
+def SignApks(input_tf_zip, output_tf_zip, apk_key_map, key_passwords):
   maxsize = max([len(os.path.basename(i.filename))
                  for i in input_tf_zip.infolist()
                  if i.filename.endswith('.apk')])
 
-  # Check that all the APKs we want to sign have keys specified, and
-  # error out if they don't.  Do this before prompting for key
-  # passwords in case we're going to fail anyway.
-  unknown_apks = []
-  for info in input_tf_zip.infolist():
-    if info.filename.endswith(".apk"):
-      name = os.path.basename(info.filename)
-      if name not in apk_key_map:
-        unknown_apks.append(name)
-  if unknown_apks:
-    print "ERROR: no key specified for:\n\n ",
-    print "\n  ".join(unknown_apks)
-    print "\nUse '-e <apkname>=' to specify a key (which may be an"
-    print "empty string to not sign this apk)."
-    sys.exit(1)
-
-  key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
-
   for info in input_tf_zip.infolist():
     data = input_tf_zip.read(info.filename)
     out_info = copy.copy(info)
@@ -289,7 +348,12 @@
   input_zip = zipfile.ZipFile(args[0], "r")
   output_zip = zipfile.ZipFile(args[1], "w")
 
-  SignApks(input_zip, output_zip)
+  apk_key_map = GetApkCerts(input_zip)
+  CheckAllApksSigned(input_zip, apk_key_map)
+  CheckSharedUserIdsConsistent(input_zip, apk_key_map)
+
+  key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
+  SignApks(input_zip, output_zip, apk_key_map, key_passwords)
 
   if OPTIONS.replace_ota_keys:
     ReplaceOtaKeys(input_zip, output_zip)