FileDocCategorySizeDatePackage
AssetAtlasService.javaAPI DocAndroid 5.1 API28825Thu Mar 12 22:22:42 GMT 2015com.android.server

AssetAtlasService

public class AssetAtlasService extends IAssetAtlas.Stub
This service is responsible for packing preloaded bitmaps into a single atlas texture. The resulting texture can be shared across processes to reduce overall memory usage.
hide

Fields Summary
public static final String
ASSET_ATLAS_SERVICE
Name of the AssetAtlasService.
private static final String
LOG_TAG
private static final boolean
DEBUG_ATLAS
private static final boolean
DEBUG_ATLAS_TEXTURE
private static final int
MIN_SIZE
private static final int
MAX_SIZE
private static final int
STEP
private static final float
PACKING_THRESHOLD
private static final int
ATLAS_MAP_ENTRY_FIELD_COUNT
private static final int
GRAPHIC_BUFFER_USAGE
private final AtomicBoolean
mAtlasReady
private final android.content.Context
mContext
private final String
mVersionName
private android.view.GraphicBuffer
mBuffer
private long[]
mAtlasMap
Constructors Summary
public AssetAtlasService(android.content.Context context)
Creates a new service. Upon creating, the service will gather the list of assets to consider for packing into the atlas and spawn a new thread to start the packing work.

param
context The context giving access to preloaded resources


                                                 
       
        mContext = context;
        mVersionName = queryVersionName(context);

        Collection<Bitmap> bitmaps = new HashSet<Bitmap>(300);
        int totalPixelCount = 0;

        // We only care about drawables that hold bitmaps
        final Resources resources = context.getResources();
        final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();

        final int count = drawables.size();
        for (int i = 0; i < count; i++) {
            try {
                totalPixelCount += drawables.valueAt(i).addAtlasableBitmaps(bitmaps);
            } catch (Throwable t) {
                Log.e("AssetAtlas", "Failed to fetch preloaded drawable state", t);
                throw t;
            }
        }

        ArrayList<Bitmap> sortedBitmaps = new ArrayList<Bitmap>(bitmaps);
        // Our algorithms perform better when the bitmaps are first sorted
        // The comparator will sort the bitmap by width first, then by height
        Collections.sort(sortedBitmaps, new Comparator<Bitmap>() {
            @Override
            public int compare(Bitmap b1, Bitmap b2) {
                if (b1.getWidth() == b2.getWidth()) {
                    return b2.getHeight() - b1.getHeight();
                }
                return b2.getWidth() - b1.getWidth();
            }
        });

        // Kick off the packing work on a worker thread
        new Thread(new Renderer(sortedBitmaps, totalPixelCount)).start();
    
Methods Summary
private booleancheckBuildIdentifier(java.io.BufferedReader reader, java.lang.String versionName)
Compares the next line in the specified buffered reader to the current build identifier. Returns whether the two values are equal.

see
#getBuildIdentifier(String)

        String deviceBuildId = getBuildIdentifier(versionName);
        String buildId = reader.readLine();
        return deviceBuildId.equals(buildId);
    
private com.android.server.AssetAtlasService$ConfigurationchooseConfiguration(java.util.ArrayList bitmaps, int pixelCount, java.lang.String versionName)
Returns the best known atlas configuration. This method will either read the configuration from disk or start a brute-force search and save the result out to disk.

        Configuration config = null;

        final File dataFile = getDataFile();
        if (dataFile.exists()) {
            config = readConfiguration(dataFile, versionName);
        }

        if (config == null) {
            config = computeBestConfiguration(bitmaps, pixelCount);
            if (config != null) writeConfiguration(config, dataFile, versionName);
        }

        return config;
    
private static com.android.server.AssetAtlasService$ConfigurationcomputeBestConfiguration(java.util.ArrayList bitmaps, int pixelCount)
Finds the best atlas configuration to pack the list of supplied bitmaps. This method takes advantage of multi-core systems by spawning a number of threads equal to the number of available cores.

        if (DEBUG_ATLAS) Log.d(LOG_TAG, "Computing best atlas configuration...");

        long begin = System.nanoTime();
        List<WorkerResult> results = Collections.synchronizedList(new ArrayList<WorkerResult>());

        // Don't bother with an extra thread if there's only one processor
        int cpuCount = Runtime.getRuntime().availableProcessors();
        if (cpuCount == 1) {
            new ComputeWorker(MIN_SIZE, MAX_SIZE, STEP, bitmaps, pixelCount, results, null).run();
        } else {
            int start = MIN_SIZE;
            int end = MAX_SIZE - (cpuCount - 1) * STEP;
            int step = STEP * cpuCount;

            final CountDownLatch signal = new CountDownLatch(cpuCount);

            for (int i = 0; i < cpuCount; i++, start += STEP, end += STEP) {
                ComputeWorker worker = new ComputeWorker(start, end, step,
                        bitmaps, pixelCount, results, signal);
                new Thread(worker, "Atlas Worker #" + (i + 1)).start();
            }

            try {
                signal.await(10, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                Log.w(LOG_TAG, "Could not complete configuration computation");
                return null;
            }
        }

        // Maximize the number of packed bitmaps, minimize the texture size
        Collections.sort(results, new Comparator<WorkerResult>() {
            @Override
            public int compare(WorkerResult r1, WorkerResult r2) {
                int delta = r2.count - r1.count;
                if (delta != 0) return delta;
                return r1.width * r1.height - r2.width * r2.height;
            }
        });

        if (DEBUG_ATLAS) {
            float delay = (System.nanoTime() - begin) / 1000.0f / 1000.0f / 1000.0f;
            Log.d(LOG_TAG, String.format("Found best atlas configuration in %.2fs", delay));
        }

        WorkerResult result = results.get(0);
        return new Configuration(result.type, result.width, result.height, result.count);
    
private static voiddeleteDataFile()

        Log.w(LOG_TAG, "Current configuration inconsistent with assets list");
        if (!getDataFile().delete()) {
            Log.w(LOG_TAG, "Could not delete the current configuration");
        }
    
public android.view.GraphicBuffergetBuffer()

        return mAtlasReady.get() ? mBuffer : null;
    
private java.lang.StringgetBuildIdentifier(java.lang.String versionName)
Returns an identifier for the current build that can be used to detect likely changes to framework resources. The build identifier is made of several distinct values: build fingerprint/framework version name/file size of framework resources apk Only the build fingerprint should be necessary on user builds but the other values are useful to detect changes on eng builds during development. This identifier does not attempt to be exact: a new identifier does not necessarily mean the preloaded drawables have changed. It is important however that whenever the list of preloaded drawables changes, this identifier changes as well.

see
#checkBuildIdentifier(java.io.BufferedReader, String)

        return SystemProperties.get("ro.build.fingerprint", "") + '/" + versionName + '/" +
                String.valueOf(getFrameworkResourcesFile().length());
    
private static java.io.FilegetDataFile()
Returns the path to the file containing the best computed atlas configuration.

        File systemDirectory = new File(Environment.getDataDirectory(), "system");
        return new File(systemDirectory, "framework_atlas.config");
    
private java.io.FilegetFrameworkResourcesFile()

        return new File(mContext.getApplicationInfo().sourceDir);
    
public long[]getMap()

        return mAtlasReady.get() ? mAtlasMap : null;
    
public booleanisCompatible(int ppid)

        return ppid == android.os.Process.myPpid();
    
private static native longnAcquireAtlasCanvas(android.graphics.Canvas canvas, int width, int height)

private static native voidnReleaseAtlasCanvas(android.graphics.Canvas canvas, long bitmap)

private static native booleannUploadAtlas(android.view.GraphicBuffer buffer, long bitmap)

private static java.lang.StringqueryVersionName(android.content.Context context)
Queries the version name stored in framework's AndroidManifest. The version name can be used to identify possible changes to framework resources.

see
#getBuildIdentifier(String)

        try {
            String packageName = context.getPackageName();
            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
            return info.versionName;
        } catch (PackageManager.NameNotFoundException e) {
            Log.w(LOG_TAG, "Could not get package info", e);
        }
        return null;
    
private com.android.server.AssetAtlasService$ConfigurationreadConfiguration(java.io.File file, java.lang.String versionName)
Reads an atlas configuration from the specified file. This method returns null if an error occurs or if the configuration is invalid.

        BufferedReader reader = null;
        Configuration config = null;
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));

            if (checkBuildIdentifier(reader, versionName)) {
                Atlas.Type type = Atlas.Type.valueOf(reader.readLine());
                int width = readInt(reader, MIN_SIZE, MAX_SIZE);
                int height = readInt(reader, MIN_SIZE, MAX_SIZE);
                int count = readInt(reader, 0, Integer.MAX_VALUE);
                int flags = readInt(reader, Integer.MIN_VALUE, Integer.MAX_VALUE);

                config = new Configuration(type, width, height, count, flags);
            }
        } catch (IllegalArgumentException e) {
            Log.w(LOG_TAG, "Invalid parameter value in " + file, e);
        } catch (FileNotFoundException e) {
            Log.w(LOG_TAG, "Could not read " + file, e);
        } catch (IOException e) {
            Log.w(LOG_TAG, "Could not read " + file, e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
        return config;
    
private static intreadInt(java.io.BufferedReader reader, int min, int max)

        return Math.max(min, Math.min(max, Integer.parseInt(reader.readLine())));
    
public voidsystemRunning()
Callback invoked by the server thread to indicate we can now run 3rd party code.

    
private voidwriteConfiguration(com.android.server.AssetAtlasService$Configuration config, java.io.File file, java.lang.String versionName)
Writes the specified atlas configuration to the specified file.

        BufferedWriter writer = null;
        try {
            writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            writer.write(getBuildIdentifier(versionName));
            writer.newLine();
            writer.write(config.type.toString());
            writer.newLine();
            writer.write(String.valueOf(config.width));
            writer.newLine();
            writer.write(String.valueOf(config.height));
            writer.newLine();
            writer.write(String.valueOf(config.count));
            writer.newLine();
            writer.write(String.valueOf(config.flags));
            writer.newLine();
        } catch (FileNotFoundException e) {
            Log.w(LOG_TAG, "Could not write " + file, e);
        } catch (IOException e) {
            Log.w(LOG_TAG, "Could not write " + file, e);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }