1 /* 2 * Copyright (C) 2024 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.internal.os; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertNotNull; 23 import static org.junit.Assert.fail; 24 import static org.junit.Assume.assumeTrue; 25 26 import android.platform.test.annotations.Presubmit; 27 28 import androidx.test.filters.SmallTest; 29 30 import org.junit.Before; 31 import org.junit.Test; 32 import org.junit.runner.RunWith; 33 import org.junit.runners.JUnit4; 34 35 import java.io.FileDescriptor; 36 import java.io.IOException; 37 38 /** Tests for {@link TimeoutRecord}. */ 39 @SmallTest 40 @Presubmit 41 @RunWith(JUnit4.class) 42 public class ApplicationSharedMemoryTest { 43 44 @Before setUp()45 public void setUp() { 46 // Skip tests if the feature under test is disabled. 47 assumeTrue(Flags.applicationSharedMemoryEnabled()); 48 } 49 50 /** 51 * Every application process, including ours, should have had an instance installed at this 52 * point. 53 */ 54 @Test hasInstance()55 public void hasInstance() { 56 // This shouldn't throw and shouldn't return null. 57 assertNotNull(ApplicationSharedMemory.getInstance()); 58 } 59 60 /** Any app process should be able to read shared memory values. */ 61 @Test canRead()62 public void canRead() { 63 ApplicationSharedMemory instance = ApplicationSharedMemory.getInstance(); 64 try { 65 instance.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(); 66 // Don't actually care about the value of the above. 67 } catch (java.time.DateTimeException e) { 68 // This exception is okay during testing. It means there was no time source, which 69 // could be because of network problems or a feature being flagged off. 70 } 71 } 72 73 /** Application processes should not have mutable access. */ 74 @Test appInstanceNotMutable()75 public void appInstanceNotMutable() { 76 ApplicationSharedMemory instance = ApplicationSharedMemory.getInstance(); 77 try { 78 instance.setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(17); 79 fail("Attempted mutation in an app process should throw"); 80 instance.writeSystemFeaturesCache(new int[] {1, 2, 3, 4, 5}); 81 fail("Attempted feature mutation in an app process should throw"); 82 } catch (Exception expected) { 83 } 84 } 85 86 /** Instances share memory if they share the underlying memory region. */ 87 @Test instancesShareMemory()88 public void instancesShareMemory() throws IOException { 89 ApplicationSharedMemory instance1 = ApplicationSharedMemory.create(); 90 ApplicationSharedMemory instance2 = 91 ApplicationSharedMemory.fromFileDescriptor( 92 instance1.getFileDescriptor(), /* mutable= */ true); 93 ApplicationSharedMemory instance3 = 94 ApplicationSharedMemory.fromFileDescriptor( 95 instance2.getReadOnlyFileDescriptor(), /* mutable= */ false); 96 97 instance1.setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(17); 98 assertEquals( 99 17, instance1.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis()); 100 assertEquals( 101 17, instance2.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis()); 102 assertEquals( 103 17, instance3.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis()); 104 105 instance2.setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(24); 106 assertEquals( 107 24, instance1.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis()); 108 assertEquals( 109 24, instance2.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis()); 110 assertEquals( 111 24, instance3.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis()); 112 } 113 114 /** Can't map read-only memory as mutable. */ 115 @Test readOnlyCantBeMutable()116 public void readOnlyCantBeMutable() throws IOException { 117 ApplicationSharedMemory readWriteInstance = ApplicationSharedMemory.create(); 118 FileDescriptor readOnlyFileDescriptor = readWriteInstance.getReadOnlyFileDescriptor(); 119 120 try { 121 ApplicationSharedMemory.fromFileDescriptor(readOnlyFileDescriptor, /* mutable= */ true); 122 fail("Shouldn't be able to map read-only memory as mutable"); 123 } catch (Exception expected) { 124 } 125 } 126 127 /** If system feature caching is enabled, it should be auto-written into app shared memory. */ 128 @Test canReadSystemFeatures()129 public void canReadSystemFeatures() throws IOException { 130 assumeTrue(android.content.pm.Flags.cacheSdkSystemFeatures()); 131 ApplicationSharedMemory instance = ApplicationSharedMemory.getInstance(); 132 assertThat(instance.readSystemFeaturesCache()).isNotEmpty(); 133 } 134 135 @Test systemFeaturesShareMemory()136 public void systemFeaturesShareMemory() throws IOException { 137 ApplicationSharedMemory instance1 = ApplicationSharedMemory.create(); 138 139 int[] featureVersions = new int[] {1, 2, 3, 4, 5}; 140 instance1.writeSystemFeaturesCache(featureVersions); 141 assertThat(featureVersions).isEqualTo(instance1.readSystemFeaturesCache()); 142 143 ApplicationSharedMemory instance2 = 144 ApplicationSharedMemory.fromFileDescriptor( 145 instance1.getReadOnlyFileDescriptor(), /* mutable= */ false); 146 assertThat(featureVersions).isEqualTo(instance2.readSystemFeaturesCache()); 147 } 148 149 @Test systemFeaturesAreWriteOnce()150 public void systemFeaturesAreWriteOnce() throws IOException { 151 ApplicationSharedMemory instance1 = ApplicationSharedMemory.create(); 152 153 try { 154 instance1.writeSystemFeaturesCache(new int[5000]); 155 fail("Cannot write an overly large system feature version buffer."); 156 } catch (IllegalArgumentException expected) { 157 } 158 159 int[] featureVersions = new int[] {1, 2, 3, 4, 5}; 160 instance1.writeSystemFeaturesCache(featureVersions); 161 162 int[] newFeatureVersions = new int[] {1, 2, 3, 4, 5, 6, 7}; 163 try { 164 instance1.writeSystemFeaturesCache(newFeatureVersions); 165 fail("Cannot update system features after first write."); 166 } catch (IllegalStateException expected) { 167 } 168 169 ApplicationSharedMemory instance2 = 170 ApplicationSharedMemory.fromFileDescriptor( 171 instance1.getReadOnlyFileDescriptor(), /* mutable= */ false); 172 try { 173 instance2.writeSystemFeaturesCache(newFeatureVersions); 174 fail("Cannot update system features for read-only ashmem."); 175 } catch (IllegalStateException expected) { 176 } 177 } 178 } 179