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