1 /* 2 * Copyright (C) 2020 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 android.app.cts; 18 19 import android.app.Service; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.ServiceConnection; 24 import android.os.AsyncTask; 25 import android.os.Binder; 26 import android.os.Bundle; 27 import android.os.IBinder; 28 import android.os.Parcel; 29 import android.os.RemoteException; 30 import android.system.ErrnoException; 31 import android.system.Os; 32 import android.system.OsConstants; 33 import android.util.Log; 34 import android.util.Pair; 35 36 import java.io.FileDescriptor; 37 import java.nio.DirectByteBuffer; 38 import java.util.concurrent.CountDownLatch; 39 import java.util.concurrent.TimeUnit; 40 import java.util.function.BooleanSupplier; 41 42 public class MemoryConsumerService extends Service { 43 private static final String TAG = MemoryConsumerService.class.getSimpleName(); 44 45 private static final int ACTION_RUN_ONCE = IBinder.FIRST_CALL_TRANSACTION; 46 private static final String EXTRA_BUNDLE = "bundle"; 47 private static final String EXTRA_TEST_FUNC = "test_func"; 48 49 private IBinder mTestFunc; 50 51 private IBinder mBinder = new Binder() { 52 @Override 53 protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) 54 throws RemoteException { 55 switch (code) { 56 case ACTION_RUN_ONCE: 57 reply.writeBoolean(fillUpMemoryAndCheck(data.readLong(), mTestFunc)); 58 return true; 59 default: 60 return false; 61 } 62 } 63 }; 64 65 static class TestFuncInterface extends Binder { 66 static final int ACTION_TEST = IBinder.FIRST_CALL_TRANSACTION; 67 68 private final BooleanSupplier mTestFunc; 69 TestFuncInterface(final BooleanSupplier testFunc)70 TestFuncInterface(final BooleanSupplier testFunc) { 71 mTestFunc = testFunc; 72 } 73 74 @Override onTransact(int code, Parcel data, Parcel reply, int flags)75 protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) 76 throws RemoteException { 77 switch (code) { 78 case ACTION_TEST: 79 reply.writeBoolean(mTestFunc.getAsBoolean()); 80 return true; 81 default: 82 return false; 83 } 84 } 85 } 86 fillUpMemoryAndCheck(final long totalMb, final IBinder testFunc)87 private boolean fillUpMemoryAndCheck(final long totalMb, final IBinder testFunc) { 88 final int pageSize = 4096; 89 final int oneMb = 1024 * 1024; 90 91 // Create an empty fd -1 92 FileDescriptor fd = new FileDescriptor(); 93 94 // Okay now start a loop to allocate 1MB each time and check if our process is gone. 95 for (long i = 0; i < totalMb; i++) { 96 long addr = 0L; 97 try { 98 addr = Os.mmap(0, oneMb, OsConstants.PROT_WRITE, 99 OsConstants.MAP_PRIVATE | OsConstants.MAP_ANONYMOUS, fd, 0); 100 } catch (ErrnoException e) { 101 Log.d(TAG, "mmap returns " + e.errno); 102 if (e.errno == OsConstants.ENOMEM) { 103 // Running out of address space? 104 return false; 105 } 106 } 107 if (addr == 0) { 108 return false; 109 } 110 111 // We don't have direct access to Memory.pokeByte() though 112 DirectByteBuffer buf = new DirectByteBuffer(oneMb, addr, fd, null, false); 113 114 // Dirt the buffer 115 for (int j = 0; j < oneMb; j += pageSize) { 116 buf.put(j, (byte) 0xf); 117 } 118 119 // Test to see if we could stop 120 Parcel data = Parcel.obtain(); 121 Parcel reply = Parcel.obtain(); 122 try { 123 testFunc.transact(TestFuncInterface.ACTION_TEST, data, reply, 0); 124 if (reply.readBoolean()) { 125 break; 126 } 127 } catch (RemoteException e) { 128 // Ignore 129 } finally { 130 data.recycle(); 131 reply.recycle(); 132 } 133 } 134 return true; 135 } 136 137 @Override onBind(Intent intent)138 public IBinder onBind(Intent intent) { 139 final Bundle bundle = intent.getBundleExtra(EXTRA_BUNDLE); 140 mTestFunc = bundle.getBinder(EXTRA_TEST_FUNC); 141 return mBinder; 142 } 143 bindToService(final Context context, final TestFuncInterface testFunc, String instanceName)144 static Pair<IBinder, ServiceConnection> bindToService(final Context context, 145 final TestFuncInterface testFunc, String instanceName) throws Exception { 146 final Intent intent = new Intent(); 147 intent.setClass(context, MemoryConsumerService.class); 148 final Bundle bundle = new Bundle(); 149 bundle.putBinder(EXTRA_TEST_FUNC, testFunc); 150 intent.putExtra(EXTRA_BUNDLE, bundle); 151 final String keyIBinder = "ibinder"; 152 final Bundle holder = new Bundle(); 153 final CountDownLatch latch = new CountDownLatch(1); 154 final ServiceConnection conn = new ServiceConnection() { 155 @Override 156 public void onServiceConnected(ComponentName name, IBinder service) { 157 holder.putBinder(keyIBinder, service); 158 latch.countDown(); 159 } 160 161 @Override 162 public void onServiceDisconnected(ComponentName name) { 163 } 164 }; 165 context.bindIsolatedService(intent, Context.BIND_AUTO_CREATE, 166 instanceName, AsyncTask.THREAD_POOL_EXECUTOR, conn); 167 latch.await(10_000, TimeUnit.MILLISECONDS); 168 return new Pair<>(holder.getBinder(keyIBinder), conn); 169 } 170 runOnce(final IBinder binder, final long totalMb)171 static boolean runOnce(final IBinder binder, final long totalMb) { 172 final Parcel data = Parcel.obtain(); 173 final Parcel reply = Parcel.obtain(); 174 175 try { 176 data.writeLong(totalMb); 177 binder.transact(ACTION_RUN_ONCE, data, reply, 0); 178 return reply.readBoolean(); 179 } catch (RemoteException e) { 180 return false; 181 } finally { 182 data.recycle(); 183 reply.recycle(); 184 } 185 } 186 } 187