1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.certinstaller; 18 19 import android.content.Intent; 20 import android.net.Uri; 21 import android.os.Bundle; 22 import android.security.Credentials; 23 import android.security.KeyChain; 24 import android.util.Log; 25 import android.widget.Toast; 26 27 import java.io.ByteArrayOutputStream; 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.util.List; 32 33 import libcore.io.IoUtils; 34 35 /** 36 * The main class for installing certificates to the system keystore. It reacts 37 * to the public {@link Credentials#INSTALL_ACTION} intent. 38 */ 39 public class CertInstallerMain extends CertFile implements Runnable { 40 @Override onCreate(Bundle savedInstanceState)41 protected void onCreate(Bundle savedInstanceState) { 42 super.onCreate(savedInstanceState); 43 if (savedInstanceState != null) { 44 return; 45 } 46 47 new Thread(new Runnable() { 48 @Override 49 public void run() { 50 // don't want to call startActivityForResult() (invoked in 51 // installFromFile()) here as it makes the new activity (thus 52 // the whole display) get stuck for about 5 seconds 53 runOnUiThread(CertInstallerMain.this); 54 } 55 }).start(); 56 } 57 58 @Override run()59 public void run() { 60 Intent intent = getIntent(); 61 String action = (intent == null) ? null : intent.getAction(); 62 63 if (Credentials.INSTALL_ACTION.equals(action) 64 || Credentials.INSTALL_AS_USER_ACTION.equals(action)) { 65 Bundle bundle = intent.getExtras(); 66 67 /* 68 * There is a special INSTALL_AS_USER action that this activity is 69 * aliased to, but you have to have a permission to call it. If the 70 * caller got here any other way, remove the extra that we allow in 71 * that INSTALL_AS_USER path. 72 */ 73 if (bundle != null && !Credentials.INSTALL_AS_USER_ACTION.equals(action)) { 74 bundle.remove(Credentials.EXTRA_INSTALL_AS_UID); 75 } 76 77 // If bundle is empty of any actual credentials, install from external storage. 78 // Otherwise, pass extras to CertInstaller to install those credentials. 79 // Either way, we use KeyChain.EXTRA_NAME as the default name if available. 80 if (bundle == null 81 || bundle.isEmpty() 82 || (bundle.size() == 1 83 && (bundle.containsKey(KeyChain.EXTRA_NAME) 84 || bundle.containsKey(Credentials.EXTRA_INSTALL_AS_UID)))) { 85 if (!isSdCardPresent()) { 86 Toast.makeText(this, R.string.sdcard_not_present, 87 Toast.LENGTH_SHORT).show(); 88 } else { 89 List<File> allFiles = getAllCertFiles(); 90 if (allFiles.isEmpty()) { 91 Toast.makeText(this, R.string.no_cert_file_found, 92 Toast.LENGTH_SHORT).show(); 93 } else if (allFiles.size() == 1) { 94 installFromFile(allFiles.get(0)); 95 return; 96 } else { 97 Intent newIntent = new Intent(this, CertFileList.class); 98 newIntent.putExtras(intent); 99 startActivityForResult(newIntent, REQUEST_INSTALL_CODE); 100 return; 101 } 102 } 103 } else { 104 Intent newIntent = new Intent(this, CertInstaller.class); 105 newIntent.putExtras(intent); 106 startActivityForResult(newIntent, REQUEST_INSTALL_CODE); 107 return; 108 } 109 } else if (Intent.ACTION_VIEW.equals(action)) { 110 Uri data = intent.getData(); 111 String type = intent.getType(); 112 if ((data != null) && (type != null)) { 113 byte[] payload = null; 114 InputStream is = null; 115 try { 116 is = getContentResolver().openInputStream(data); 117 ByteArrayOutputStream out = new ByteArrayOutputStream(); 118 byte[] buffer = new byte[1024]; 119 int read = 0; 120 while ((read = is.read(buffer)) > 0) { 121 out.write(buffer, 0, read); 122 } 123 out.flush(); 124 payload = out.toByteArray(); 125 } catch (IOException ignored) { 126 // Not much we can do - it will be logged below as an error. 127 } finally { 128 IoUtils.closeQuietly(is); 129 } 130 if (payload == null) { 131 Log.e("CertInstaller", "Unable to read stream for for certificate"); 132 } else { 133 installByType(type, payload); 134 } 135 } 136 } 137 finish(); 138 } 139 installByType(String type, byte[] value)140 private void installByType(String type, byte[] value) { 141 Intent intent = new Intent(this, CertInstaller.class); 142 if ("application/x-pkcs12".equals(type)) { 143 intent.putExtra(KeyChain.EXTRA_PKCS12, value); 144 } else if ("application/x-x509-ca-cert".equals(type) 145 || "application/x-x509-user-cert".equals(type)) { 146 intent.putExtra(KeyChain.EXTRA_CERTIFICATE, value); 147 } else { 148 throw new AssertionError("Unknown type: " + type); 149 } 150 startActivityForResult(intent, REQUEST_INSTALL_CODE); 151 } 152 153 @Override onInstallationDone(boolean success)154 protected void onInstallationDone(boolean success) { 155 super.onInstallationDone(success); 156 finish(); 157 } 158 159 @Override onError(int errorId)160 protected void onError(int errorId) { 161 finish(); 162 } 163 } 164