1 /*
2  * Copyright (C) 2022 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.tests.statsd.framework.appnopermission;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertThrows;
21 
22 import android.app.PendingIntent;
23 import android.app.StatsManager;
24 import android.app.StatsManager.StatsUnavailableException;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import androidx.test.InstrumentationRegistry;
29 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
30 import org.junit.Test;
31 
32 public class StatsdPermissionTest {
33   /**
34    * Broadcast receiver to test registration of a FetchOperation.
35    */
36   public static final class SimpleReceiver extends BroadcastReceiver {
37     @Override
onReceive(Context mContext, Intent intent)38     public void onReceive(Context mContext, Intent intent) {}
39   }
40 
41   /**
42    * Tests that calling apis without permissions throws the appropriate exception.
43    */
44   @Test
testApisThrowExceptionWithoutPermission()45   public void testApisThrowExceptionWithoutPermission() throws Exception {
46     Context context = InstrumentationRegistry.getTargetContext();
47     StatsManager statsManager = context.getSystemService(StatsManager.class);
48     StatsUnavailableException e = assertThrows(StatsManager.StatsUnavailableException.class,
49         ()
50             -> statsManager.addConfig(1234,
51                 StatsdConfig.newBuilder()
52                     .setId(1234)
53                     .addAllowedLogSource("foo")
54                     .build()
55                     .toByteArray()));
56     assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
57 
58     e = assertThrows(
59         StatsManager.StatsUnavailableException.class, () -> statsManager.removeConfig(1234));
60     assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
61 
62     e = assertThrows(
63         StatsManager.StatsUnavailableException.class, () -> statsManager.getReports(1234));
64     // expectations are:
65     // - for Android T+ receive IllegalStateException
66     // - for previous versions receive SecurityExceptions
67     assertThat(e.getCause().getClass()).
68             isAnyOf(SecurityException.class, IllegalStateException.class);
69 
70     e = assertThrows(
71         StatsManager.StatsUnavailableException.class, () -> statsManager.getStatsMetadata());
72     assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
73 
74     e = assertThrows(StatsManager.StatsUnavailableException.class,
75         () -> statsManager.getRegisteredExperimentIds());
76     assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
77 
78     PendingIntent pi = PendingIntent.getBroadcast(
79         context, 1, new Intent(context, SimpleReceiver.class), PendingIntent.FLAG_MUTABLE);
80 
81     e = assertThrows(StatsManager.StatsUnavailableException.class,
82         () -> statsManager.setBroadcastSubscriber(pi, 123, 0));
83     assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
84 
85     e = assertThrows(StatsManager.StatsUnavailableException.class,
86         () -> statsManager.setBroadcastSubscriber(null, 123, 0));
87     assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
88 
89     e = assertThrows(StatsManager.StatsUnavailableException.class,
90         () -> statsManager.setFetchReportsOperation(pi, 123));
91     assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
92 
93     e = assertThrows(StatsManager.StatsUnavailableException.class,
94         () -> statsManager.setFetchReportsOperation(null, 123));
95     assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
96 
97     e = assertThrows(StatsManager.StatsUnavailableException.class,
98         () -> statsManager.setActiveConfigsChangedOperation(pi));
99     assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
100 
101     e = assertThrows(StatsManager.StatsUnavailableException.class,
102         () -> statsManager.setActiveConfigsChangedOperation(null));
103     assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
104 
105     // Setting a pull atom callback is not tested because it is oneway,
106     // so the security exception would not propagate to the client.
107   }
108 }
109