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
17package com.android.certinstaller;
18
19import android.content.Intent;
20import android.net.Uri;
21import android.os.Bundle;
22import android.security.Credentials;
23import android.security.KeyChain;
24import android.util.Log;
25import android.widget.Toast;
26
27import java.io.ByteArrayOutputStream;
28import java.io.File;
29import java.io.IOException;
30import java.io.InputStream;
31import java.util.List;
32
33import 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 */
39public class CertInstallerMain extends CertFile implements Runnable {
40    @Override
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
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            Bundle bundle = intent.getExtras();
65            // If bundle is empty of any actual credentials, install from external storage.
66            // Otherwise, pass extras to CertInstaller to install those credentials.
67            // Either way, we use KeyChain.EXTRA_NAME as the default name if available.
68            if (bundle == null
69                    || bundle.isEmpty()
70                    || (bundle.size() == 1 && bundle.containsKey(KeyChain.EXTRA_NAME))) {
71                if (!isSdCardPresent()) {
72                    Toast.makeText(this, R.string.sdcard_not_present,
73                            Toast.LENGTH_SHORT).show();
74                } else {
75                    List<File> allFiles = getAllCertFiles();
76                    if (allFiles.isEmpty()) {
77                        Toast.makeText(this, R.string.no_cert_file_found,
78                                Toast.LENGTH_SHORT).show();
79                    } else if (allFiles.size() == 1) {
80                        installFromFile(allFiles.get(0));
81                        return;
82                    } else {
83                        Intent newIntent = new Intent(this, CertFileList.class);
84                        newIntent.putExtras(intent);
85                        startActivityForResult(newIntent, REQUEST_INSTALL_CODE);
86                        return;
87                    }
88                }
89            } else {
90                Intent newIntent = new Intent(this, CertInstaller.class);
91                newIntent.putExtras(intent);
92                startActivityForResult(newIntent, REQUEST_INSTALL_CODE);
93                return;
94            }
95        } else if (Intent.ACTION_VIEW.equals(action)) {
96            Uri data = intent.getData();
97            String type = intent.getType();
98            if ((data != null) && (type != null)) {
99                byte[] payload = null;
100                InputStream is = null;
101                try {
102                    is = getContentResolver().openInputStream(data);
103                    ByteArrayOutputStream out = new ByteArrayOutputStream();
104                    byte[] buffer = new byte[1024];
105                    int read = 0;
106                    while ((read = is.read(buffer)) > 0) {
107                        out.write(buffer, 0, read);
108                    }
109                    out.flush();
110                    payload = out.toByteArray();
111                } catch (IOException ignored) {
112                    // Not much we can do - it will be logged below as an error.
113                } finally {
114                    IoUtils.closeQuietly(is);
115                }
116                if (payload == null) {
117                    Log.e("CertInstaller", "Unable to read stream for for certificate");
118                } else {
119                    installByType(type, payload);
120                }
121            }
122        }
123        finish();
124    }
125
126    private void installByType(String type, byte[] value) {
127        Intent intent = new Intent(this, CertInstaller.class);
128        if ("application/x-pkcs12".equals(type)) {
129            intent.putExtra(KeyChain.EXTRA_PKCS12, value);
130        } else if ("application/x-x509-ca-cert".equals(type)
131                || "application/x-x509-user-cert".equals(type)) {
132            intent.putExtra(KeyChain.EXTRA_CERTIFICATE, value);
133        } else {
134            throw new AssertionError("Unknown type: " + type);
135        }
136        startActivityForResult(intent, REQUEST_INSTALL_CODE);
137    }
138
139    @Override
140    protected void onInstallationDone(boolean success) {
141        super.onInstallationDone(success);
142        finish();
143    }
144
145    @Override
146    protected void onError(int errorId) {
147        finish();
148    }
149}
150