1/*
2 * Copyright (C) 2017 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 */
16package com.android.internal.telephony;
17
18import android.app.DownloadManager;
19import android.content.Context;
20import android.content.Intent;
21import android.content.SharedPreferences;
22import android.os.HandlerThread;
23import android.os.PersistableBundle;
24import android.telephony.CarrierConfigManager;
25import android.telephony.ImsiEncryptionInfo;
26import android.test.suitebuilder.annotation.SmallTest;
27import android.util.Pair;
28
29import com.android.org.bouncycastle.util.io.pem.PemReader;
30
31import org.junit.After;
32import org.junit.Before;
33import org.junit.Test;
34import org.mockito.Matchers;
35import org.mockito.MockitoAnnotations;
36
37import java.io.BufferedReader;
38import java.io.ByteArrayInputStream;
39import java.io.InputStreamReader;
40import java.io.Reader;
41import java.security.PublicKey;
42import java.text.SimpleDateFormat;
43import java.util.Calendar;
44import java.util.Date;
45import java.util.GregorianCalendar;
46
47import static android.preference.PreferenceManager.getDefaultSharedPreferences;
48
49import static org.junit.Assert.assertFalse;
50import static org.junit.Assert.assertTrue;
51import static org.junit.Assert.fail;
52import static org.mockito.ArgumentMatchers.any;
53import static org.mockito.Mockito.anyInt;
54import static org.mockito.Mockito.times;
55import static org.mockito.Mockito.verify;
56import static org.mockito.Mockito.when;
57
58public class CarrierKeyDownloadMgrTest extends TelephonyTest {
59
60    private static final String LOG_TAG = "CarrierKeyDownloadManager";
61
62    private CarrierKeyDownloadManager mCarrierKeyDM;
63    private CarrierActionAgentHandler mCarrierActionAgentHandler;
64
65    private String mURL = "http://www.google.com";
66
67    private static final String CERT = "-----BEGIN CERTIFICATE-----\r\nMIIFjzCCBHegAwIBAgIUPxj3SLif82Ky1RlUy8p2EWJCh8MwDQYJKoZIhvcNAQELBQAwgY0xCzAJBgNVBAYTAk5MMRIwEAYDVQQHEwlBbXN0ZXJkYW0xJTAjBgNVBAoTHFZlcml6b24gRW50ZXJwcmlzZSBTb2x1dGlvbnMxEzARBgNVBAsTCkN5YmVydHJ1c3QxLjAsBgNVBAMTJVZlcml6b24gUHVibGljIFN1cmVTZXJ2ZXIgQ0EgRzE0LVNIQTIwHhcNMTcwODE0MTc0MzM4WhcNMTkwODE0MTc0MzM4WjCBmTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFjAUBgNVBAcTDUJhc2tpbmcgUmlkZ2UxIjAgBgNVBAoTGVZlcml6b24gRGF0YSBTZXJ2aWNlcyBMTEMxHzAdBgNVBAsTFk5ldHdvcmsgU3lzdGVtIFN1cHBvcnQxGDAWBgNVBAMTD3ZpMWx2Lmltc3ZtLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALUQKWTHi4Hjpd1LQwJ87RXa0Rs3rVonvVevliqdUH5BikjhAzvIqwPSXeRQqkaRTFIyp0NKcNqGdjAaHRo43gdHeWSH331sS6CMZDg988gZznskzCqJJo6ii5FuLC8qe2YDsHxT+CefXev2rn6Bj1ei2X74uZsy5KlkBRZfFHtPdK6/EK5TpzrvcXfDyOK1rn8FTno1bQOTAhL39GPcLhdrXV7AN+lu+EBpdCqlTdcoDxsqavi/91MwUIVEzxJmycKloT6OWfU44r7+L5SYYgc88NTaGL/BvCFwHRIa1ZgYSGeAPes45792MGG7tfr/ttAGp9UEwTv2zWTxzWnRP/UCAwEAAaOCAdcwggHTMAwGA1UdEwEB/wQCMAAwTAYDVR0gBEUwQzBBBgkrBgEEAbE+ATIwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly9zZWN1cmUub21uaXJvb3QuY29tL3JlcG9zaXRvcnkwgakGCCsGAQUFBwEBBIGcMIGZMC0GCCsGAQUFBzABhiFodHRwOi8vdnBzc2cxNDIub2NzcC5vbW5pcm9vdC5jb20wMwYIKwYBBQUHMAKGJ2h0dHA6Ly9jYWNlcnQub21uaXJvb3QuY29tL3Zwc3NnMTQyLmNydDAzBggrBgEFBQcwAoYnaHR0cDovL2NhY2VydC5vbW5pcm9vdC5jb20vdnBzc2cxNDIuZGVyMBoGA1UdEQQTMBGCD3ZpMWx2Lmltc3ZtLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFOQtu5EBZSYftHo/oxUlpM6MRDM7MD4GA1UdHwQ3MDUwM6AxoC+GLWh0dHA6Ly92cHNzZzE0Mi5jcmwub21uaXJvb3QuY29tL3Zwc3NnMTQyLmNybDAdBgNVHQ4EFgQUv5SaSyNM/yXw1v0N9TNpjsFCaPcwDQYJKoZIhvcNAQELBQADggEBACNJusTULj1KyV4RwiskKfp4wI9Hsz3ESbZS/ijF9D57BQ0UwkELU9r6rEAhsYLUvMq4sDhDbYIdupgP4MBzFnjkKult7VQm5W3nCcuHgXYFAJ9Y1a4OZAo/4hrHj70W9TsQ1ioSMjUT4F8bDUYZI0kcyH8e/+2DaTsLUpHw3L+Keu8PsJVBLnvcKJjWrZD/Bgd6JuaTX2G84i0rY0GJuO9CxLNJa6n61Mz5cqLYIuwKgiVgTA2n71YITyFICOFPFX1vSx35AWvD6aVYblxtC8mpCdF2h4s1iyrpXeji2GCJLwsNVtTtNQ4zWX3Gnq683wzkYZeyOHUyftIgAQZ+HsY=\r\n-----END CERTIFICATE-----";
68
69
70    private String mJsonStr = "{ \"carrier-keys\": [ { \"certificate\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }, { \"certificate\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }]}";
71
72    private String mJsonStr1 = "{ \"carrier-keys\": [ { \"public-key\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }, { \"public-key\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }]}";
73
74    private class CarrierActionAgentHandler extends HandlerThread {
75
76        private CarrierActionAgentHandler(String name) {
77            super(name);
78        }
79
80        @Override
81        public void onLooperPrepared() {
82            mCarrierKeyDM = new CarrierKeyDownloadManager(mPhone);
83            setReady(true);
84        }
85    }
86
87    @Before
88    public void setUp() throws Exception {
89        logd("CarrierActionAgentTest +Setup!");
90        MockitoAnnotations.initMocks(this);
91        super.setUp(getClass().getSimpleName());
92        mCarrierActionAgentHandler = new CarrierActionAgentHandler(getClass().getSimpleName());
93        mCarrierActionAgentHandler.start();
94        waitUntilReady();
95        logd("CarrierActionAgentTest -Setup!");
96    }
97
98    @After
99    public void tearDown() throws Exception {
100        mCarrierActionAgentHandler.quit();
101        super.tearDown();
102    }
103
104    /* Checks if the expiration date is calculated correctly
105     * In this case the expiration date should be the next day.
106     */
107    @Test
108    @SmallTest
109    public void testExpirationDate1Day() {
110        java.security.PublicKey publicKey = null;
111        mCarrierKeyDM.mKeyAvailability = 3;
112        SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
113        Calendar cal = new GregorianCalendar();
114        cal.add(Calendar.DATE, 6);
115        Date date = cal.getTime();
116        Calendar expectedCal = new GregorianCalendar();
117        expectedCal.add(Calendar.DATE, 1);
118        String dateExpected = dt.format(expectedCal.getTime());
119        ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("mcc", "mnc", 1,
120                "keyIdentifier", publicKey, date);
121        when(mPhone.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(imsiEncryptionInfo);
122        Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
123        assertTrue(dt.format(expirationDate).equals(dateExpected));
124    }
125
126    /**
127     * Checks if the expiration date is calculated correctly
128     * In this case the expiration date should be within the window (7 to 21 days).
129     **/
130    @Test
131    @SmallTest
132    public void testExpirationDate7Day() {
133        java.security.PublicKey publicKey = null;
134        mCarrierKeyDM.mKeyAvailability = 3;
135        SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
136        Calendar cal = new GregorianCalendar();
137        cal.add(Calendar.DATE, 30);
138        Date date = cal.getTime();
139        Calendar minExpirationCal = new GregorianCalendar();
140        Calendar maxExpirationCal = new GregorianCalendar();
141        minExpirationCal.add(Calendar.DATE, 23);
142        maxExpirationCal.add(Calendar.DATE, 9);
143        Date minExpirationDate = minExpirationCal.getTime();
144        Date maxExpirationDate = maxExpirationCal.getTime();
145        ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("mcc", "mnc", 1,
146                "keyIdentifier", publicKey, date);
147        when(mPhone.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(imsiEncryptionInfo);
148        Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
149        assertTrue(expirationDate.before(minExpirationDate));
150        assertTrue(expirationDate.after(maxExpirationDate));
151    }
152
153    /**
154     * Checks if the json is parse correctly.
155     * Verify that setCarrierInfoForImsiEncryption is called with the right params
156     **/
157    @Test
158    @SmallTest
159    public void testParseJson() {
160        ByteArrayInputStream certBytes = new ByteArrayInputStream(CERT.getBytes());
161        Reader fRd = new BufferedReader(new InputStreamReader(certBytes));
162        PemReader reader = new PemReader(fRd);
163        Pair<PublicKey, Long> keyInfo = null;
164        try {
165            keyInfo = mCarrierKeyDM.getKeyInformation(reader.readPemObject().getContent());
166        } catch (Exception e) {
167            fail(LOG_TAG + "exception creating public key");
168        }
169        ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", 2,
170                "key1=value", keyInfo.first, new Date(keyInfo.second));
171        String mccMnc = "310:270";
172        mCarrierKeyDM.parseJsonAndPersistKey(mJsonStr, mccMnc);
173        verify(mPhone, times(2)).setCarrierInfoForImsiEncryption(
174                (Matchers.refEq(imsiEncryptionInfo)));
175    }
176
177    /**
178     * Checks if the json is parse correctly.
179     * Same as testParseJason, except that the test looks for the "public-key" field.
180     **/
181    @Test
182    @SmallTest
183    public void testParseJsonPublicKey() {
184        ByteArrayInputStream certBytes = new ByteArrayInputStream(CERT.getBytes());
185        Reader fRd = new BufferedReader(new InputStreamReader(certBytes));
186        PemReader reader = new PemReader(fRd);
187        Pair<PublicKey, Long> keyInfo = null;
188        try {
189            keyInfo = mCarrierKeyDM.getKeyInformation(reader.readPemObject().getContent());
190        } catch (Exception e) {
191            fail(LOG_TAG + "exception creating public key");
192        }
193        ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", 2,
194                "key1=value", keyInfo.first, new Date(keyInfo.second));
195        String mccMnc = "310:270";
196        mCarrierKeyDM.parseJsonAndPersistKey(mJsonStr1, mccMnc);
197        verify(mPhone, times(2)).setCarrierInfoForImsiEncryption(
198                (Matchers.refEq(imsiEncryptionInfo)));
199    }
200
201    /**
202     * Checks if the json is parse correctly.
203     * Since the json is bad, we want to verify that savePublicKey is not called.
204     **/
205    @Test
206    @SmallTest
207    public void testParseBadJsonFail() {
208        String mccMnc = "310:290";
209        String badJsonStr = "{badJsonString}";
210        mCarrierKeyDM.parseJsonAndPersistKey(badJsonStr, mccMnc);
211        verify(mPhone, times(0)).setCarrierInfoForImsiEncryption(any());
212    }
213
214    /**
215     * Checks if the download is valid.
216     * returns true since the mnc/mcc is valid.
217     **/
218    @Test
219    @SmallTest
220    public void testIsValidDownload() {
221        String mccMnc = "310:260";
222        when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
223        assertTrue(mCarrierKeyDM.isValidDownload(mccMnc));
224    }
225
226    /**
227     * Checks if the download is valid.
228     * returns false since the mnc/mcc is in-valid.
229     **/
230    @Test
231    @SmallTest
232    public void testIsValidDownloadFail() {
233        String mccMnc = "310:290";
234        when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
235        assertFalse(mCarrierKeyDM.isValidDownload(mccMnc));
236    }
237
238    /**
239     * Tests if the key is enabled.
240     * tests for all bit-mask value.
241     **/
242    @Test
243    @SmallTest
244    public void testIsKeyEnabled() {
245        mCarrierKeyDM.mKeyAvailability = 3;
246        assertTrue(mCarrierKeyDM.isKeyEnabled(1));
247        assertTrue(mCarrierKeyDM.isKeyEnabled(2));
248        mCarrierKeyDM.mKeyAvailability = 2;
249        assertFalse(mCarrierKeyDM.isKeyEnabled(1));
250        assertTrue(mCarrierKeyDM.isKeyEnabled(2));
251        mCarrierKeyDM.mKeyAvailability = 1;
252        assertTrue(mCarrierKeyDM.isKeyEnabled(1));
253        assertFalse(mCarrierKeyDM.isKeyEnabled(2));
254    }
255
256    /**
257     * Tests sending the ACTION_DOWNLOAD_COMPLETE intent.
258     * Verify that the alarm will kick-off the next day.
259     **/
260    @Test
261    @SmallTest
262    public void testDownloadComplete() {
263        SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
264        String mccMnc = "310:260";
265        int slotId = mPhone.getPhoneId();
266        editor.putString("CARRIER_KEY_DM_MCC_MNC" + slotId, mccMnc);
267        editor.commit();
268
269        SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
270        Calendar expectedCal = new GregorianCalendar();
271        expectedCal.add(Calendar.DATE, 1);
272        String dateExpected = dt.format(expectedCal.getTime());
273
274        when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
275        Intent mIntent = new Intent(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
276        mContext.sendBroadcast(mIntent);
277        Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
278        assertTrue(dt.format(expirationDate).equals(dateExpected));
279    }
280
281    /**
282     * Test sending the ACTION_CARRIER_CONFIG_CHANGED intent.
283     * Verify that the right mnc/mcc gets stored in the preferences.
284     **/
285    @Test
286    @SmallTest
287    public void testCarrierConfigChanged() {
288        CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
289                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
290        int slotId = mPhone.getPhoneId();
291        PersistableBundle bundle = carrierConfigManager.getConfigForSubId(slotId);
292        bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
293        bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
294
295        when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
296        Intent mIntent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
297        mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
298        mContext.sendBroadcast(mIntent);
299        SharedPreferences preferences = getDefaultSharedPreferences(mContext);
300        String mccMnc = preferences.getString("CARRIER_KEY_DM_MCC_MNC" + slotId, null);
301        assertTrue(mccMnc.equals("310:260"));
302    }
303
304    /**
305     * Tests sending the INTENT_KEY_RENEWAL_ALARM_PREFIX intent.
306     * Verify that the right mnc/mcc gets stored in the preferences.
307     **/
308    @Test
309    @SmallTest
310    public void testAlarmRenewal() {
311        CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
312                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
313        int slotId = mPhone.getPhoneId();
314        PersistableBundle bundle = carrierConfigManager.getConfigForSubId(slotId);
315        bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
316        bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
317
318        when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
319        Intent mIntent = new Intent("com.android.internal.telephony.carrier_key_download_alarm"
320                + slotId);
321        mContext.sendBroadcast(mIntent);
322        SharedPreferences preferences = getDefaultSharedPreferences(mContext);
323        String mccMnc = preferences.getString("CARRIER_KEY_DM_MCC_MNC" + slotId, null);
324        assertTrue(mccMnc.equals("310:260"));
325    }
326}
327