Categories
discuss

System error capturing the output of a MediaProjection virtual display to an ImageReader

I am working on an application that needs to capture the screen to a bitmap to transmit. I am attempting to use the new Android 5.0 android.media.projection APIs to do the screen capture.

The workflow for this API culminates in a call to

mediaProjection.createVirtualDisplay("Test Screen", WIDTH, HEIGHT, DPI,
   DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null);

In my initial attempt at this capture I sourced the surface object from a SurfaceView. This works correctly; the end result is a tiny duplicate of the display being drawn on-screen (resulting in a Droste Effect)

I thought the feature nearly complete, but I then discovered that SurfaceViews are (from a code standpoint) not readable; you cannot get a bitmap from them.

In looking for other solutions I came across this question which has a very similar goal to mine, and in that thread it is suggested to use an ImageReader instead of a SurfaceView to source the Surface that you pass to the createVirtualDisplay API call.

However, when I change my code to use an ImageReader in lieu of a SurfaceView I get runtime logcat errors (no exceptions), and the callback function for the ImageReader never gets called. The createVirtualDisplay call also returns a seemingly valid VirtualDisplay object.

Here is the logcat:

9230-9270/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: createGraphicBuffer failed
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count

That second line repeats ~100 times before it stops occurring.

Stepping through on the debugger I see that the first error occurs during the createVirtualDisplay call, and all the others happen some point after execution returns to system code.

The only meaningful result for this error relates to an issue in Kitkat, where the API I am trying to consume does not exist. Nonetheless, I tried the fix suggested here (putting android:hardwareAccelerated="false" in the manifest). This did not change the application’s behavior.

How can I “set the buffer count” or otherwise work around this error and get the screen as a bitmap?

P.S. My development platform is the Nexus 6.

The full code block, as requested:

MediaProjection mediaProjection = mgr.getMediaProjection(resultCode, data);
ImageReader ir = ImageReader.newInstance(WIDTH, HEIGHT, ImageFormat.JPEG, 5);
VirtualDisplay v = mediaProjection.createVirtualDisplay("Test Screen", WIDTH, HEIGHT, getApplicationContext().getResources().getDisplayMetrics().densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, ir.getSurface(), null, null);

Edit: Regarding the artifact issue, here is the code I am using to get the bitmap out of the image and display it:

 public void onImageAvailable(ImageReader reader) {
        Image image = null;
        ByteArrayOutputStream bos = null;

        try {
            image = reader.acquireLatestImage();
            if (null == image){
                return;
            }
            bos = new ByteArrayOutputStream();
            final Image.Plane[] planes = image.getPlanes();
            final ByteBuffer buffer = (ByteBuffer) planes[0].getBuffer().rewind();
            final Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
            bitmap.copyPixelsFromBuffer(buffer);
            //bitmap.compress(Bitmap.CompressFormat.WEBP, 50, bos);

            runOnUiThread(new Runnable() {
                public void run() {
                    iv.setImageBitmap(bitmap);
                }
            });

Answer

I think I can answer this question now, I met the same problem and after I change ImageFormat.JPEG to PixelFormat.RGBA_8888 everything goes well. It seems ImageFormat.JPEG is not supported.

You need to use the following code to get the correct bitmap:

                    int width = img.getWidth();
                    int height = img.getHeight();
                    int pixelStride = planes[0].getPixelStride();
                    int rowStride = planes[0].getRowStride();
                    int rowPadding = rowStride - pixelStride * width;
                    byte[] newData = new byte[width * height * 4];

                    int offset = 0;
                    bitmap = Bitmap.createBitmap(metrics,width, height, Bitmap.Config.ARGB_8888);
                    ByteBuffer buffer = planes[0].getBuffer();
                    for (int i = 0; i < height; ++i) {
                        for (int j = 0; j < width; ++j) {
                            int pixel = 0;
                            pixel |= (buffer.get(offset) & 0xff) << 16;     // R
                            pixel |= (buffer.get(offset + 1) & 0xff) << 8;  // G
                            pixel |= (buffer.get(offset + 2) & 0xff);       // B
                            pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A
                            bitmap.setPixel(j, i, pixel);
                            offset += pixelStride;
                        }
                        offset += rowPadding;
                    }

From this way, the content of bitmap is what you want.

PS: I really want to say, the doc of android is pretty bad. we need to investigate too much detail to use sdk api correctly.

Categories
discuss

Is there a possibility to find out the technology used for creating a mobile application?

I have discovered it is possible to find out how a website is built by using services like Wappalyzer, SimilarTech or BuiltWith (they check HTTP headers, ping responses, cookies, comments and other fancy things). Now I am wondering: can you find how a mobile application (Android, iOS) is built by using similar techniques / services? I know that the code is compiled and one should not try to reverse engineer it (for obvious copyright reasons)… but can it be done without “breaking the rules” (I mean by at least discovering how a service works, not the code / algorithm behind it?)

Disclaimer: This is only for educational and security reasons.

Answer

Disclaimer: As you mention, reverse engineering is usually not permitted by EULAs and may be illegal in some countries. Make sure that you know what rules apply before venturing into this area.

For iOS you can reverse engineer binaries using thing like iOS Reverse Engineering Toolkit and Hopper. This does of course not produce the same level of code as the actual source code but it can provide some insight into how applications are made.

If you’re interested in finding how communication works, there are a few ways to monitor network traffic from an iPhone using a computer. This is done using RVI which is a bit of a hassle. Apple has a guide on it (Search for RVI on the page) and I found this blog providing some detail. I’ve only used it once and I remember it being a pain to set up in Wireshark with no good details provided on how to configure it, you’ll have to tinker a bit with the settings to get it right.

When it comes to Java there’s a tonne of resources out there on reverse-engineering and it’s generally a lot easier than compiled code, however obfuscation does of course make it harder. JAD is one of the more popular decompilers.

Categories
discuss

How to find if two numbers are consecutive numbers in gray code sequence

I am trying to come up with a solution to the problem that given two numbers, find if they are the consecutive numbers in the gray code sequence i.e., if they are gray code neighbors assuming that the gray code sequence is not mentioned.

I searched on various forums but couldn’t get the right answer. It would be great if you can provide a solution for this.

My attempt to the problem – Convert two integers to binary and add the digits in both the numbers separately and find the difference between the sum of the digits in two numbers. If the difference is one then they are gray code neighbors.

But I feel this wont work for all cases. Any help is highly appreciated. Thanks a lot in advance!!!

Answer

I’ve had to solve this question in an interview as well. One of the conditions for the two values to be a gray code sequence is that their values only differ by 1 bit. Here is a solution to this problem:

def isGrayCode(num1, num2):
    differences = 0
    while (num1 > 0 or num2 > 0):
        if ((num1 & 1) != (num2 & 1)):
            differences++
        num1 >>= 1
        num2 >>= 1
    return differences == 1
Categories
discuss

What happens in Spring if I use the @ActiveProfiles annotation on a configuration class instead use it on the class that defines my beans?

I am studying for the Spring Core certification and I have some doubts related to the use of profiles into JUnit tests.

So I know that if I annote a class in the following way:

@Profile("stub")
@Repository
public class StubAccountRepository implements AccountRepository {

    private Logger logger = Logger.getLogger(StubAccountRepository.class);

    private Map<String, Account> accountsByCreditCard = new HashMap<String, Account>();

    /**
     * Creates a single test account with two beneficiaries. Also logs creation
     * so we know which repository we are using.
     */
    public StubAccountRepository() {
        logger.info("Creating " + getClass().getSimpleName());
        Account account = new Account("123456789", "Keith and Keri Donald");
        account.addBeneficiary("Annabelle", Percentage.valueOf("50%"));
        account.addBeneficiary("Corgan", Percentage.valueOf("50%"));
        accountsByCreditCard.put("1234123412341234", account);
    }

    public Account findByCreditCard(String creditCardNumber) {
        Account account = accountsByCreditCard.get(creditCardNumber);
        if (account == null) {
            throw new EmptyResultDataAccessException(1);
        }
        return account;
    }

    public void updateBeneficiaries(Account account) {
        // nothing to do, everything is in memory
    }
}

I am declaring a service bean that belongs to the stub profile.

So, if my test class is something like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=TestInfrastructureConfig.class)
@ActiveProfiles("stub")
public class RewardNetworkTests {
    .....................................
    .....................................
    .....................................
}

it means that it will be used the beans bean that belong to the stub profile and the bean that not have a profile. Is it right or am I missing something?

What happens if instead use the @ActiveProfiles annotation on a class (whose instance will be a Spring bean) I use it on a Java Configuration Class?

Something like it:

@Configuration
@Profile("jdbc-dev")
public class TestInfrastructureDevConfig {

    /**
     * Creates an in-memory "rewards" database populated 
     * with test data for fast testing
     */
    @Bean
    public DataSource dataSource(){
        return
            (new EmbeddedDatabaseBuilder())
            .addScript("classpath:rewards/testdb/schema.sql")
            .addScript("classpath:rewards/testdb/test-data.sql")
            .build();
    }   
}

What exactly do? I think that all the beans configured in this class will belong to the jdbc-dev profile, but I am not sure about it. Can you give me more information about this thing?

Why I have to use the @Profile annotation on a **configuration class* instead annotate directly my beans?

Tnx

Answer

If you look at the JavaDoc of ActiveProfiles annotation, it contains this text:

ActiveProfiles is a class-level annotation that is used to declare which active bean definition profiles should be used when loading an ApplicationContext for test classes.

Meaning it is only supposed to be used to declare active Spring profiles for test classes. So If put it on a Configuration class it should have no effect.

And as for the @Profile annotation, it can be used on both method and class level. If you use it on method annotated with @Bean in configuration class, only that bean will belong to the profile. If you use it on configuration class, it will be applied to all the beans within the configuration class, if you use it on @Component class, the profile will be applied to the bean represented by that class.

@Profile annotation JavaDoc provides more detailed explanation of these rules.

Why I have to use the @Profile annotation on a **configuration class* instead annotate directly my beans?

Well if all beans in given configuration class should be active only for certain profile(s) then it makes sense to declare that globally on the configuration class to avoid having to individually specify the profile on all beans. But If you were to annotate all indiviudal beans it would work as well.

Categories
discuss

TreeMap and setValue method

In the java.util.TreeMap javadoc there is this statement:

All Map.Entry pairs returned by methods in this class and its views represent snapshots of mappings at the time they were produced. They do not support the Entry.setValue method. (Note however that it is possible to change mappings in the associated map using put.)

I don’t get this line. In what way they do not support setValue method? When I use entrySet() and iterate over Map.Entry object it sets value fine.

    Map<String, Integer> map = new TreeMap<>();
    map.put("dbc", 1);
    map.put("abc", 1);
    map.put("cbc", 1);
    for(Map.Entry<String, Integer> item: map.entrySet()) {
        item.setValue(1);
    }

Answer

This is a known issue.

  • There is an OpenJDK tracker entry (JDK-8038146) that notes that this is a javadoc error. But there is more to it than this.

  • There is also a Java Bug Database entry (bug id 7006877) that explains that the javadoc was changed to say that in Java 6, and that it is actually true for the alternative version of TreeMap that you get (got) if you run the JVM with aggressive optimizations enabled.

    This ticket also says that the issue affected Java 7, and was fixed in Java 8. They apparently removed the alternative TreeMap implementation … though they didn’t change the javadoc.


Commentary:

If the issue trackers are to be believed (and I’ve understood them correctly), then the javadoc probably ought to say that the Entry.setValue method may not be supported in Java 6 and Java 7. But the misleading sentences could be entirely removed for Java 8 onwards.

Whether that is the correct thing to do is somewhat debatable, because some people need to understand how their new Java code might run on older platforms. Maybe it would be best to leave this as a historical footnote.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..