1 /*
2  * Copyright (C) 2007 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.server.content;
18 
19 import static org.mockito.ArgumentMatchers.any;
20 import static org.mockito.ArgumentMatchers.anyBoolean;
21 import static org.mockito.ArgumentMatchers.anyInt;
22 import static org.mockito.ArgumentMatchers.argThat;
23 import static org.mockito.ArgumentMatchers.eq;
24 import static org.mockito.Mockito.mock;
25 import static org.mockito.Mockito.times;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.app.ActivityManager;
30 import android.app.ActivityManagerInternal;
31 import android.content.ContentResolver;
32 import android.database.ContentObserver;
33 import android.database.IContentObserver;
34 import android.net.Uri;
35 import android.os.Binder;
36 import android.os.Handler;
37 import android.os.Looper;
38 import android.os.UserHandle;
39 import android.util.ArraySet;
40 
41 import androidx.test.runner.AndroidJUnit4;
42 
43 import com.android.server.LocalServices;
44 import com.android.server.content.ContentService.ObserverCollector;
45 import com.android.server.content.ContentService.ObserverNode;
46 
47 import org.junit.Test;
48 import org.junit.runner.RunWith;
49 import org.mockito.ArgumentMatcher;
50 
51 import java.util.Arrays;
52 
53 /**
54  * atest FrameworksServicesTests:com.android.server.content.ObserverNodeTest
55  */
56 @RunWith(AndroidJUnit4.class)
57 public class ObserverNodeTest {
58     static class TestObserver extends ContentObserver {
TestObserver()59         public TestObserver() {
60             super(new Handler(Looper.getMainLooper()));
61         }
62     }
63 
64     @Test
testUri()65     public void testUri() {
66         final int myUserHandle = UserHandle.myUserId();
67 
68         ObserverNode root = new ObserverNode("");
69         Uri[] uris = new Uri[] {
70             Uri.parse("content://c/a/"),
71             Uri.parse("content://c/"),
72             Uri.parse("content://x/"),
73             Uri.parse("content://c/b/"),
74             Uri.parse("content://c/a/a1/1/"),
75             Uri.parse("content://c/a/a1/2/"),
76             Uri.parse("content://c/b/1/"),
77             Uri.parse("content://c/b/2/"),
78         };
79 
80         int[] nums = new int[] {4, 7, 1, 4, 2, 2, 3, 3};
81 
82         // special case
83         root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root,
84                 0, 0, myUserHandle);
85         for(int i = 1; i < uris.length; i++) {
86             root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root,
87                     0, 0, myUserHandle);
88         }
89 
90         for (int i = nums.length - 1; i >=0; --i) {
91             final ObserverCollector collector = mock(ObserverCollector.class);
92             root.collectObserversLocked(uris[i], 0, null, false, 0, myUserHandle, collector);
93             verify(collector, times(nums[i])).collect(
94                     any(), anyInt(), anyBoolean(), any(), anyInt(), anyInt());
95         }
96     }
97 
98     @Test
testUriNotNotify()99     public void testUriNotNotify() {
100         final int myUserHandle = UserHandle.myUserId();
101 
102         ObserverNode root = new ObserverNode("");
103         Uri[] uris = new Uri[] {
104             Uri.parse("content://c/"),
105             Uri.parse("content://x/"),
106             Uri.parse("content://c/a/"),
107             Uri.parse("content://c/b/"),
108             Uri.parse("content://c/a/1/"),
109             Uri.parse("content://c/a/2/"),
110             Uri.parse("content://c/b/1/"),
111             Uri.parse("content://c/b/2/"),
112         };
113         int[] nums = new int[] {7, 1, 3, 3, 1, 1, 1, 1};
114 
115         for(int i = 0; i < uris.length; i++) {
116             root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root,
117                     0, 0, myUserHandle);
118         }
119 
120         for (int i = uris.length - 1; i >=0; --i) {
121             final ObserverCollector collector = mock(ObserverCollector.class);
122             root.collectObserversLocked(uris[i], 0, null, false, 0, myUserHandle, collector);
123             verify(collector, times(nums[i])).collect(
124                     any(), anyInt(), anyBoolean(), any(), anyInt(), anyInt());
125         }
126     }
127 
128     @Test
testCluster()129     public void testCluster() throws Exception {
130         final int myUserHandle = UserHandle.myUserId();
131 
132         // Assume everything is foreground during our test
133         final ActivityManagerInternal ami = mock(ActivityManagerInternal.class);
134         when(ami.getUidProcessState(anyInt()))
135                 .thenReturn(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
136         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
137         LocalServices.addService(ActivityManagerInternal.class, ami);
138 
139         final IContentObserver observer = mock(IContentObserver.class);
140         when(observer.asBinder()).thenReturn(new Binder());
141 
142         final ObserverNode root = new ObserverNode("");
143         root.addObserverLocked(Uri.parse("content://authority/"), observer,
144                 true, root, 0, 1000, myUserHandle);
145 
146         final ObserverCollector collector = new ObserverCollector();
147         root.collectObserversLocked(Uri.parse("content://authority/1"), 0, null, false,
148                 0, myUserHandle, collector);
149         root.collectObserversLocked(Uri.parse("content://authority/1"), 0, null, false,
150                 ContentResolver.NOTIFY_INSERT, myUserHandle, collector);
151         root.collectObserversLocked(Uri.parse("content://authority/2"), 0, null, false,
152                 ContentResolver.NOTIFY_INSERT, myUserHandle, collector);
153         root.collectObserversLocked(Uri.parse("content://authority/2"), 0, null, false,
154                 ContentResolver.NOTIFY_UPDATE, myUserHandle, collector);
155         collector.dispatch();
156 
157         // We should only cluster when all other arguments are equal
158         verify(observer).onChangeEtc(eq(false), argThat(new UriSetMatcher(
159                         Uri.parse("content://authority/1"))),
160                 eq(0), anyInt());
161         verify(observer).onChangeEtc(eq(false), argThat(new UriSetMatcher(
162                         Uri.parse("content://authority/1"),
163                         Uri.parse("content://authority/2"))),
164                 eq(ContentResolver.NOTIFY_INSERT), anyInt());
165         verify(observer).onChangeEtc(eq(false), argThat(new UriSetMatcher(
166                         Uri.parse("content://authority/2"))),
167                 eq(ContentResolver.NOTIFY_UPDATE), anyInt());
168     }
169 
170     private static class UriSetMatcher implements ArgumentMatcher<Uri[]> {
171         private final ArraySet<Uri> uris;
172 
UriSetMatcher(Uri... uris)173         public UriSetMatcher(Uri... uris) {
174             this.uris = new ArraySet<>(Arrays.asList(uris));
175         }
176 
177         @Override
matches(Uri[] uris)178         public boolean matches(Uri[] uris) {
179             final ArraySet<Uri> test = new ArraySet<>(Arrays.asList(uris));
180             return this.uris.equals(test);
181         }
182     }
183 }
184